sng tenant
Deployed and supported. The fields and commands described here are stable within version v1alpha1.
The sng tenant group manages your tenants and the session that gives you access to them: listing, creating, entering a tenant (use), switching from one tenant to another (assume), destroying it. Its subgroups set who can do what (policy), the size of the tenant (tier), the trusted CI pipelines (oidc), and the account that pays for the tenant (billing).
sng tenant create acme
sng tenant use acme
sng tenant policy attach readonly --user bob --with-assume
The model (identity, session, tenant boundary) is explained in The identity, session, tenant model. The global flags (--config, --json, -v, --version) are described in the CLI index.
Two tokens, two families of commands.
list,create, andusepresent your identity (sng sso login). Without it:not authenticated: run sng sso login.- All other commands present the tenant session (1 h). The CLI reopens it on its own with your identity when it expires or is refused (
401), and saves the new one. Without a session:tenant session required: run sng tenant use NAME.
IAM actions per command:
| Command | IAM action | Resource | Route |
|---|---|---|---|
list | none (identity) | — | GET /v1/tenants |
create | none (platform organization) | — | POST /v1/tenants |
use | iam:assumerole (policy of the targeted tenant) | tenant/<tenant> | POST /v1/iam/session |
assume | iam:assumerole (policy of the targeted tenant) | tenant/<tenant> | POST /v1/iam/session/assume |
destroy | iam:admin | tenant/<tenant> | DELETE /v1/tenants/{tenant} |
policy list | policy:list | policy/* | GET /v1/iam/policies |
policy get, policy principals | policy:read | policy/<nom> | GET /v1/iam/policies/{name}[/principals] |
policy create, update, delete | policy:admin | policy/<nom> | PUT / DELETE /v1/iam/policies/{name} |
policy attach, detach | policy:admin | policy/<nom> | POST /v1/iam/policies/{name}/attach / detach |
tier list | tier:list | tier/<tenant> | GET /v1/tiers |
tier show | tier:read | tier/<tenant> | GET /v1/tier |
tier set | tier:write | tier/<tenant> | PUT /v1/tier |
oidc list | oidctrust:list | oidctrust/* | GET /v1/iam/oidc-trusts |
oidc get | oidctrust:read | oidctrust/<nom> | GET /v1/iam/oidc-trusts/{name} |
oidc create, delete | oidctrust:admin | oidctrust/<nom> | GET then PUT / DELETE /v1/iam/oidc-trusts/{name} |
billing get, set | iam:admin | tenant/<tenant> | GET /v1/billing/tenants/{tenant} / POST /v1/tenants/{tenant}/billing-owner/invitations |
Details of each action are in the action catalog.
sng tenant list
sng tenant list [--json]
Lists the tenants where you hold at least one policy, directly or through one of your SSO roles. The current tenant is marked *.
| Flag | Type | Default | Description |
|---|---|---|---|
| (no flags of its own) |
sng tenant list
TENANT POLICIES CURRENT
acme admin,assume-role *
acme-staging assume-role,readonly
Without tenants: no tenants. Being in the list is not enough to enter: you need assume-role (see use).
--json output: {"tenants": [{"tenant": "acme", "policies": ["admin", "assume-role"]}]}.
sng tenant create
sng tenant create NAME
Creates a tenant. You become its administrator: its default policies bind you to assume-role and admin within a few minutes. The command is open to users of the platform's organization; a machine does not create tenants.
| Argument | Description |
|---|---|
NAME | tenant name: lowercase, digits, and -, starts with a letter or a digit, at most 30 characters |
The name also becomes a domain name and an identity directory: environment names (dev, prod, staging…), platform component names (api, auth, iam, console…), generic terms (admin, tenant, default…), and the prefixes kube-, stackngo-, sng- are reserved.
sng tenant create acme
tenant "acme" created: its default policies (assume-role and admin for you) are usually applied within a minute
next: sng tenant use acme
--json output: {"name": "acme"}.
Typical errors:
| Message | Cause |
|---|---|
reserved name (400) | reserved name |
bad request (400) | invalid or too long name |
conflict (409) | the tenant already exists, or the name is already taken on the platform |
forbidden … (403) | your account does not belong to the platform's organization |
… lookup unavailable (503) | the name cannot be checked for now: try again |
sng tenant use
sng tenant use NAME
Opens a session on the tenant NAME with your identity, saves it in the configuration file, and makes it the current tenant. The session lasts 1 h; the CLI then renews it on its own.
A policy of the targeted tenant must grant you iam:assumerole on tenant/NAME (managed policy assume-role). A tenant created less than 5 minutes ago responds 409 policies_pending: the command waits and retries for up to 6 minutes, at the pace given by Retry-After.
| Argument | Description |
|---|---|
NAME | targeted tenant |
sng tenant use acme
waiting for the tenant default policies to be applied...
current tenant: acme
The first line, on standard error, only appears for a brand-new tenant.
Typical errors:
| Message | Cause |
|---|---|
no assume-role on tenant "acme": ask an admin to run `sng tenant policy attach assume-role -t acme --user YOU` (403) | no policy of acme grants you iam:assumerole: ask an administrator to run the command shown, with your user name. With -v, the API response follows the message |
tenant policies not applied yet; retry `sng tenant use acme` in a minute (409) | new tenant, still pending after 6 minutes: run sng tenant use again a little later |
not authenticated: run sng sso login | identity missing, expired, or refused (401) |
unknown tenant (404) | the tenant does not exist |
sng tenant assume
sng tenant assume NAME [--print]
Exchanges the current session for a session on NAME (POST /v1/iam/session/assume), without replaying your identity. This is the path for scripts and machines, which only have a session. A policy of NAME must grant iam:assumerole to the subject of the current session. The new session keeps track of the origin tenant (origin_tenant) and never expires after the session it comes from.
| Argument | Description |
|---|---|
NAME | targeted tenant |
| Flag | Type | Default | Description |
|---|---|---|---|
--print | bool | false | prints the session token instead of changing the current tenant |
sng tenant assume acme-staging
TOKEN=$(sng tenant assume acme-staging --print)
current tenant: acme-staging (assumed from acme)
--json output (without --print): {"tenant": "acme-staging", "origin_tenant": "acme"}.
Typical errors: no assume-role on tenant "acme-staging" for the current session subject (403, no policy of acme-staging grants iam:assumerole to the session's subject; -v adds the API response); tenant session required: run sng tenant use NAME (no session to exchange); unknown tenant (404).
assumeThe current tenant becomes NAME. On expiry, the CLI renews the session with your identity on NAME (like sng tenant use NAME), not through a new assume.
sng tenant destroy
sng tenant destroy NAME [--yes]
Destroys a tenant and everything it contains: identity directory (sng iam), secrets, desired state and deployment history, policies. Irreversible. Asks for confirmation (type oui) unless --yes is given.
You need a session on that tenant (sng tenant use NAME first) and the iam:admin action on tenant/NAME (admin policy).
| Argument | Description |
|---|---|
NAME | tenant to destroy; must be the current tenant |
| Flag | Type | Default | Description |
|---|---|---|---|
--yes, -y | bool | false | confirm without an interactive prompt |
sng tenant use acme-old
sng tenant destroy acme-old
⚠ Permanently destroy tenant "acme-old" (users, applications, secrets, resources).
Tapez « oui » pour confirmer : oui
tenant "acme-old" destroyed
The current tenant is then cleared: run sng tenant use on another tenant.
Typical errors: session tenant is "acme"; run `sng tenant use acme-old` first; confirmation requise : relancez avec --yes ou depuis un terminal interactif; forbidden (403, no iam:admin).
sng tenant policy
Deployed and supported. The fields and commands described here are stable within version v1alpha1.
sng tenant policy (list | get | create | update | delete | attach | detach | principals) [-t TENANT]
Manages the tenant's policies: Effect / Actions / Resources documents that say who can do what, and their attachments to subjects. The document format is described in Write a policy; subjects in Attach a policy.
Writes are accepted and then applied within a few minutes: the status moves from pending to applied. Managed policies (assume-role, readonly, readwrite, admin, plus those the platform uses for its own services) can be neither modified nor deleted, but can be attached.
Flag common to all subcommands:
| Flag | Type | Default | Description |
|---|---|---|---|
--tenant, -t | string | (current tenant) | targeted tenant; the CLI opens a session on that tenant for that command only, without changing the current tenant (you need iam:assumerole there) |
sng tenant policy list
sng tenant policy list [-t TENANT] [--json]
Lists the tenant's policies, managed ones included.
sng tenant policy list
NAME DEFAULT STATUS VERSION DESCRIPTION
admin yes applied 1 Accès complet au tenant, y compris la gestion des utilisateurs, des rôles et des policies.
assume-role yes applied 1 Permet d'ouvrir une session sur ce tenant.
ops-read pending lecture des secrets hors production
readonly yes applied 1 Lecture seule : consulter les ressources du tenant sans rien modifier.
readwrite yes applied 1 Lecture et écriture des ressources du tenant (secrets, déploiements, sites, images), sans gestion des accès.
…
Managed policies carry a description written by the platform (in French); the ones reserved to the platform are marked « Réservée à la plateforme … Ne pas attacher. » (reserved to the platform, do not attach).
| Status | Meaning |
|---|---|
applied | the requested document is in effect |
pending | created or modified, not applied yet |
deleting | deletion requested, policy still in effect for a few moments |
Without policies: no policies.
--json output: an array [{"name": "…", "description": "…", "default": false, "status": "applied", "version": 2, "document": {…}}].
sng tenant policy get
sng tenant policy get NAME [-t TENANT] [--json]
Prints the policy's JSON document. With --json, the full object: name, description, default, status, version, document.
| Argument | Description |
|---|---|
NAME | policy name |
sng tenant policy get readonly
{
"Version": "1",
"Statement": [
{
"Effect": "Allow",
"Actions": ["*:read", "*:list", "*:describe", "deploy:logs", "registry:pull", "GET", "HEAD"],
"Resources": ["*"]
}
]
}
Error: not found (404).
sng tenant policy create
sng tenant policy create NAME -f FILE [--description TEXT] [-t TENANT]
Creates a policy. Refuses a policy that already exists.
| Argument | Description |
|---|---|
NAME | name: lowercase, digits, and -, at most 63 characters; neither a managed policy name nor the bind- prefix |
| Flag | Type | Default | Description |
|---|---|---|---|
--file, -f | string | (required) | policy document in JSON or YAML, output of policy get --json, or an IAMPolicy manifest; - reads standard input |
--description | string | description; otherwise, the one in the file (description, or spec.description of a manifest) |
sng tenant policy create ops-read -f ops-read.json --description "lecture des secrets hors production"
sng tenant policy create ops-read -f ops-read.yml # IAMPolicy manifest
policy "ops-read" created (pending: usually applied within a minute)
--json output: the policy as accepted, status: "pending".
Typical errors:
| Message | Cause |
|---|---|
policy "ops-read" already exists: use `sng tenant policy update ops-read -f FILE` | the policy exists (including a managed policy) |
ops-read.json: not a JSON or YAML object | unreadable file |
IAMPolicy: spec.statement is empty | manifest without a statement |
policies: invalid request: … (400) | invalid document (effect, action, resource, limit) or invalid name |
policies: reserved: … (409) | name of a managed policy, or system tenant |
sng tenant policy update
sng tenant policy update NAME -f FILE [--description TEXT] [-t TENANT]
Replaces the document of an existing policy. Same flags and formats as create.
policy "ops-read" updated (pending: usually applied within a minute)
Specific error: policy "x" does not exist: use `sng tenant policy create x -f FILE` . On a managed policy: 409.
sng tenant policy delete
sng tenant policy delete NAME [--yes] [-t TENANT]
Deletes a policy and its attachment (bind-<nom>). Asks for confirmation unless --yes is given.
| Flag | Type | Default | Description |
|---|---|---|---|
--yes, -y | bool | false | confirm without an interactive prompt |
sng tenant policy delete ops-read --yes
policy "ops-read" deletion requested (usually applied within a minute)
Errors: not found (404); 409 on a managed policy.
sng tenant policy attach
sng tenant policy attach NAME [--user U]... [--role ORG/NOM]...
[--app NOM]... [--oidc-trust TRUST]...
[--with-assume] [-t TENANT]
Attaches a policy (yours or a managed one) to subjects. Cumulative: the subjects are added to those already attached. This is also how you add someone to a tenant (--with-assume).
| Argument | Description |
|---|---|
NAME | policy to attach |
| Flag | Type | Default | Description |
|---|---|---|---|
--user | string, repeatable | SSO user: nom, org/nom, or email | |
--role | string, repeatable | SSO role, org/NOM form required | |
--app | string, repeatable | machine application, SSO name (status.provider.name of the IAMApplication) | |
--oidc-trust | string, repeatable | OIDC trust of the tenant, name only (without /) — Beta | |
--with-assume | bool | false | also attaches the assume-role policy to the same subjects (needed for a user to open a session) |
sng tenant policy attach readonly --user bob --with-assume
sng tenant policy attach readwrite --role stackngo/dev --with-assume
sng tenant policy attach ops-read --app sng-1a2b3c4d-5e6f7a8b-app-ci-deployer
policy "readonly" bound to user:bob (pending: usually applied within a minute)
policy "assume-role" bound to user:bob (pending: usually applied within a minute)
Typical errors:
| Message | Cause |
|---|---|
at least one --user, --role, … is required | no subject |
--role "dev": expected org/NAME (e.g. stackngo/platform-admin) | role without an organization |
--oidc-trust "acme/ci": expected a trust NAME of the tenant (see `sng tenant oidc list`) | trust name with / |
subjects must be explicit (no empty value, no global *) | empty value or * |
not found (404) | the policy does not exist in the tenant |
forbidden: insufficient permissions (no policy of this tenant allows it: …) (403) | no policy:admin |
With --with-assume, the CLI first attaches NAME, then assume-role: if the second write fails, the first one remains done (the bound to line already shown proves it).
sng tenant policy detach
sng tenant policy detach NAME [mêmes flags de sujets] [--with-assume] [-t TENANT]
Removes subjects from a policy. --with-assume also removes them from assume-role. Removing the last subject deletes the attachment.
sng tenant policy detach readonly --user bob --with-assume
policy "readonly" unbound from user:bob (pending: usually applied within a minute)
policy "assume-role" unbound from user:bob (pending: usually applied within a minute)
sng tenant policy principals
sng tenant policy principals NAME [-t TENANT] [--json]
Shows who the policy is attached to: the applied principals (resolved identifiers; a user appears by their sub) and the requested subjects.
sng tenant policy principals readonly
STATE KIND ID
applied user 7f3e2c1a-0b5d-4c1e-9a77-2d0c4e8f1b23
requested user bob
Without an attachment: not bound.
--json output: {"principals": [{"kind": "user", "id": "…"}], "desired": {"users": ["bob"]}} (desired is null without a requested attachment).
sng tenant tier
Being rolled out. The interface described here is the one of the next release: it may still change, and may not be enabled on your tenant yet.
Being enabled gradually on tenants.
sng tenant tier (list | show | set TIER) [-t TENANT]
The tier bounds the resources of your tenant: guaranteed CPU and memory (requests.*), ceilings (limits.*), storage and number of instances. Grid, from smallest to largest: 3XS, 2XS, XS, S, M, L, XL, 2XL, 3XL; the former names XXS, XXL and XXXL are still accepted and mean 3XS, 2XL and 3XL. The catalog comes from the API; the CLI only ever sends the tier name. A new tenant starts in 3XS. Values: Tiers and quotas. Moving up is always allowed; moving down is refused as long as the current consumption does not fit in the requested tier.
| Flag | Type | Default | Description |
|---|---|---|---|
--tenant, -t | string | (current tenant) | targeted tenant |
sng tenant tier list
sng tenant tier list
TIER REQUESTS.CPU REQUESTS.MEMORY LIMITS.CPU LIMITS.MEMORY …
3XS 500m 1Gi 1 2Gi …
2XS 500m 1Gi 2 4Gi …
XS 1 2Gi 4 8Gi …
S 4 8Gi 4 8Gi …
…
The output of your sng tenant tier list is authoritative. The last column gives the maximum number of instances.
--json output: {"tiers": [{"name": "3XS", "hard": {"requests.cpu": "500m", …}}], "keys": ["requests.cpu", "requests.memory", "limits.cpu", "limits.memory", …]}.
sng tenant tier show
sng tenant tier show
tenant acme: tier 3XS
RESOURCE USED HARD
requests.cpu 250m 500m
requests.memory 512Mi 1Gi
limits.cpu 500m 1
limits.memory 1Gi 2Gi
…
Right after a tier change, as long as the consumption has not been computed yet, USED is - and the command adds usage not available yet (quota not synced).
--json output: {"tenant": "acme", "tier": "3XS", "hard": {…}, "used": {…}, "keys": […]} (used absent if unavailable).
sng tenant tier set
sng tenant tier set TIER [-t TENANT]
Changes the tier (case-insensitive name; XXS, XXL and XXXL mean 3XS, 2XL and 3XL). Requires tier:write: the admin policy, not readwrite.
sng tenant tier set S
tenant acme: tier 3XS -> S (…)
If the tenant is already on that tier: tenant acme: already on tier S.
--json output: {"tenant": "acme", "tier": "S", "previous": "3XS"}.
Typical errors:
| Message | Cause |
|---|---|
unknown tier "4XL" (see GET /v1/tiers) (400) | name not in the catalog |
tier too small: cannot downgrade to 3XS: requests.memory 1536Mi > 1Gi (409) | the current consumption exceeds the requested tier |
tier usage unavailable (503) | consumption unreadable: a downgrade is refused as a precaution, try again |
forbidden (…) (403) | no tier:write |
sng tenant oidc
Being rolled out. The interface described here is the one of the next release: it may still change, and may not be enabled on your tenant yet.
CI pipeline federation is being rolled out gradually.
sng tenant oidc (list | get NAME | create NAME | delete NAME)
Manages the OIDC trusts of the current tenant (IAMOIDCTrust): the GitLab CI or GitHub Actions pipelines allowed to open a session with their job's OIDC token, with no stored secret. A trust grants no rights: attach a policy to it with sng tenant policy attach POLICY --oidc-trust NAME. The full guide is Deploy without secrets (OIDC).
These commands always act on the session's tenant: they have no -t flag.
sng tenant oidc list
sng tenant oidc list
NAME ISSUER SUBJECT STATUS
gitlab-deploy https://gitlab.com project_path:acme/infra:ref_type:branch:ref:main applied
Without trusts: no OIDC trusts. Statuses: applied, pending, deleting.
--json output: an array [{"name": "…", "status": "…", "version": 1, "spec": {…}}].
sng tenant oidc get
sng tenant oidc get gitlab-deploy
name: gitlab-deploy
status: applied
issuer: https://gitlab.com
audiences: https://api.dev.stackngo.fr
subject: project_path:acme/infra:ref_type:branch:ref:main
claims: project_id=4242; ref_protected=true
max session: 1h (default)
discovery: ok
The discovery line gives the result of the issuer check, made when the trust is applied:
ok, or failed: <cause> (retried on use) if the issuer did not answer (the check is made
again at each token exchange). It is absent as long as the trust has not been checked yet
(status pending).
sng tenant oidc create
sng tenant oidc create NAME (-f FILE | --gitlab-project GROUP/PROJECT | --github-repo ORG/REPO | --issuer URL --subject MOTIF)
[--ref BRANCHE] [--gitlab-url URL] [--audience AUD]... [--claim K=V1,V2]...
[--max-session DURÉE]
Declares a trust. Refuses a name already in use (delete the old one first).
| Argument | Description |
|---|---|
NAME | trust name: lowercase, digits, and -, at most 63 characters |
| Flag | Type | Default | Description |
|---|---|---|---|
--file, -f | string | IAMOIDCTrust manifest (without a specified tenant), output of oidc get --json, or spec alone, in JSON or YAML; - reads standard input; mutually exclusive with all other flags | |
--gitlab-project | string | GitLab helper: issuer = --gitlab-url, subject project_path:GROUP/PROJECT:ref_type:branch:ref:REF | |
--gitlab-url | string | https://gitlab.com | GitLab instance (with --gitlab-project only) |
--github-repo | string | GitHub helper: issuer https://token.actions.githubusercontent.com, subject repo:ORG/REPO:ref:refs/heads/REF | |
--ref | string | main | trusted branch for the helpers; * = all branches |
--issuer | string | any issuer, over https:// (with --subject, without a helper) | |
--subject | string | sub pattern (* wildcard, at least 8 literal characters before the first *) | |
--audience | string, repeatable | (API URL) | accepted audience |
--claim | CLÉ=V1,V2, repeatable | required claim: AND between keys, OR between values | |
--max-session | duration | 1h | lifetime of the sessions opened by this trust, from 5m to 1h |
sng tenant oidc create gitlab-deploy --gitlab-project acme/infra --ref main \
--claim project_id=4242 --claim ref_protected=true
sng tenant oidc create github-deploy --github-repo acme/infra --claim repository_id=123456789
sng tenant oidc create ci -f ci-trust.yml
OIDC trust "gitlab-deploy" created in tenant "acme" (pending: …)
subject: project_path:acme/infra:ref_type:branch:ref:main
next: sng tenant policy attach POLICY --oidc-trust gitlab-deploy
--json output: the accepted trust, status: "pending".
Typical errors: -f and --gitlab-project are mutually exclusive; --gitlab-project and --github-repo are mutually exclusive; --gitlab-project generates issuer and subject: drop --issuer; --ref only applies with --gitlab-project or --github-repo; -f FILE, --issuer and --subject, --gitlab-project or --github-repo is required; subject "*": must not start with '*'; maxSessionDuration "2h": must be between 5m and 1h; ci-trust.yml: metadata.… (acme) is not allowed: … (tenant specified in the manifest); OIDC trust "gitlab-deploy" already exists in tenant "acme": delete it first; policies: invalid request: at most 20 oidc trusts per tenant (400).
sng tenant oidc delete
sng tenant oidc delete NAME [--yes]
Deletes a trust: the CI jobs that rely on it can no longer open a session. Sessions already open last until they expire. The attachments that mention it remain, with no effect.
| Flag | Type | Default | Description |
|---|---|---|---|
--yes, -y | bool | false | confirm without an interactive prompt |
OIDC trust "gitlab-deploy" deletion requested (…)
sng tenant billing
Being rolled out. The interface described here is the one of the next release: it may still change, and may not be enabled on your tenant yet.
sng tenant billing get TENANT
sng tenant billing set TENANT EMAIL
Reserved for tenant administrators. A tenant is billed to a StackNGO account, its payer: its creator at first, then the account that accepts an invitation to take over billing. get shows the payer (name and masked e-mail), the pending invitation and, if any, the deletion date of a tenant nobody pays any more. set invites an address: nothing changes until the invited account accepts.
| Argument | Description |
|---|---|
TENANT | tenant concerned |
EMAIL | verified address of the invited account |
sng tenant billing get acme
sng tenant billing set acme jane@example.com
TENANT TIER PAYER E-MAIL PENDING NOTICE
acme 3XS Jane (you) j***@example.com bob@example.com (until 2026-11-16) -
If a Stackngo account matches this address, an invitation has been sent to it.
If a StackNGO account holds this verified address, it receives a link valid 7 days; the answer is the same in every case. A new invitation replaces the pending one. The payer gets no right on the tenant.
--json output of get: {"tenant": "acme", "tier": "3XS", "payer": {"name": "Jane", "email": "j***@example.com", "isYou": true}, "pending": {…} | null, "notice": {"deleteAt": "…"} | null}. Without the tenant admin policy: 403.
See also
- Guides: The identity, session, tenant model, Write a policy, Attach a policy, Action catalog, Machines and technical accounts, Deploy without secrets (OIDC)
- Manifests: IAMPolicy, IAMPolicyBinding, IAMOIDCTrust
- Related commands:
sng sso,sng iam - API: Authentication