Skip to main content

sng tenant

Available

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, and use present 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:

CommandIAM actionResourceRoute
listnone (identity)—GET /v1/tenants
createnone (platform organization)—POST /v1/tenants
useiam:assumerole (policy of the targeted tenant)tenant/<tenant>POST /v1/iam/session
assumeiam:assumerole (policy of the targeted tenant)tenant/<tenant>POST /v1/iam/session/assume
destroyiam:admintenant/<tenant>DELETE /v1/tenants/{tenant}
policy listpolicy:listpolicy/*GET /v1/iam/policies
policy get, policy principalspolicy:readpolicy/<nom>GET /v1/iam/policies/{name}[/principals]
policy create, update, deletepolicy:adminpolicy/<nom>PUT / DELETE /v1/iam/policies/{name}
policy attach, detachpolicy:adminpolicy/<nom>POST /v1/iam/policies/{name}/attach / detach
tier listtier:listtier/<tenant>GET /v1/tiers
tier showtier:readtier/<tenant>GET /v1/tier
tier settier:writetier/<tenant>PUT /v1/tier
oidc listoidctrust:listoidctrust/*GET /v1/iam/oidc-trusts
oidc getoidctrust:readoidctrust/<nom>GET /v1/iam/oidc-trusts/{name}
oidc create, deleteoidctrust:adminoidctrust/<nom>GET then PUT / DELETE /v1/iam/oidc-trusts/{name}
billing get, setiam:admintenant/<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 *.

FlagTypeDefaultDescription
(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.

ArgumentDescription
NAMEtenant 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:

MessageCause
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.

ArgumentDescription
NAMEtargeted 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:

MessageCause
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 loginidentity 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.

ArgumentDescription
NAMEtargeted tenant
FlagTypeDefaultDescription
--printboolfalseprints 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).

Renewal after assume

The 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).

ArgumentDescription
NAMEtenant to destroy; must be the current tenant
FlagTypeDefaultDescription
--yes, -yboolfalseconfirm 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​

Available

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:

FlagTypeDefaultDescription
--tenant, -tstring(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).

StatusMeaning
appliedthe requested document is in effect
pendingcreated or modified, not applied yet
deletingdeletion 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.

ArgumentDescription
NAMEpolicy 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.

ArgumentDescription
NAMEname: lowercase, digits, and -, at most 63 characters; neither a managed policy name nor the bind- prefix
FlagTypeDefaultDescription
--file, -fstring(required)policy document in JSON or YAML, output of policy get --json, or an IAMPolicy manifest; - reads standard input
--descriptionstringdescription; 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:

MessageCause
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 objectunreadable file
IAMPolicy: spec.statement is emptymanifest 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.

FlagTypeDefaultDescription
--yes, -yboolfalseconfirm 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).

ArgumentDescription
NAMEpolicy to attach
FlagTypeDefaultDescription
--userstring, repeatableSSO user: nom, org/nom, or email
--rolestring, repeatableSSO role, org/NOM form required
--appstring, repeatablemachine application, SSO name (status.provider.name of the IAMApplication)
--oidc-truststring, repeatableOIDC trust of the tenant, name only (without /) — Beta
--with-assumeboolfalsealso 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:

MessageCause
at least one --user, --role, … is requiredno 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​

Beta

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.

FlagTypeDefaultDescription
--tenant, -tstring(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:

MessageCause
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​

Beta

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).

ArgumentDescription
NAMEtrust name: lowercase, digits, and -, at most 63 characters
FlagTypeDefaultDescription
--file, -fstringIAMOIDCTrust 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-projectstringGitLab helper: issuer = --gitlab-url, subject project_path:GROUP/PROJECT:ref_type:branch:ref:REF
--gitlab-urlstringhttps://gitlab.comGitLab instance (with --gitlab-project only)
--github-repostringGitHub helper: issuer https://token.actions.githubusercontent.com, subject repo:ORG/REPO:ref:refs/heads/REF
--refstringmaintrusted branch for the helpers; * = all branches
--issuerstringany issuer, over https:// (with --subject, without a helper)
--subjectstringsub pattern (* wildcard, at least 8 literal characters before the first *)
--audiencestring, repeatable(API URL)accepted audience
--claimCLÉ=V1,V2, repeatablerequired claim: AND between keys, OR between values
--max-sessionduration1hlifetime 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.

FlagTypeDefaultDescription
--yes, -yboolfalseconfirm without an interactive prompt
OIDC trust "gitlab-deploy" deletion requested (…)

sng tenant billing​

Beta

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.

ArgumentDescription
TENANTtenant concerned
EMAILverified 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​