Declare a secret by manifest
Deployed and supported. The fields and commands described here are stable within version v1alpha1.
With an SSMSecret manifest, the secret is part of the description of your tenant, like your Apps: it is created when the manifest is applied, its rotation is declared in the spec, and it is deleted with the manifest. The value, however, never goes through Git: the platform generates it, or you set it with sng secret set.
Prerequisites
- The CLI configured to apply manifests: cluster and tenant chosen (
sng config use-cluster,sng tenant use). See The validate, plan, apply cycle. - The right to apply manifests in your tenant (policy
readwriteoradmin). - No particular right on secrets is needed to declare: the platform creates the secret for you. To read the generated value, you need
ssm:read.
1. Declare a secret with a generated value
sng secret create db-password --length 24 --rotation 30
secret "db-password" declared in tenant acme-dev: …
acme-dev is the current tenant of your sng configuration. The command produced a manifest and applied it, exactly like sng apply:
apiVersion: ssm.stackngo.io/v1alpha1
kind: SSMSecret
metadata:
name: db-password
spec:
length: 24
rotateDays: 30
To write it yourself and keep it in your repository, add --dry-run: the manifest is printed without being applied.
sng secret create db-password --length 24 --rotation 30 --dry-run > secrets/db-password.yml
sng apply secrets/db-password.yml
sng template gen SSMSecret db-password produces the same skeleton, with comments.
Less than a minute after the apply, the secret has a value:
sng secret describe db-password
{
"name": "db-password",
"keys": ["value"],
"version_id": "1",
"rotation_enabled": true,
"rotation_days": 30,
"next_rotation_date": "2026-11-06T10:02:17Z",
"version_stages": { "current": "1" }
}
2. Declare a secret whose value you set
When the value comes from elsewhere (a provider's API key, the password of an external service), declare the secret empty with --length 0, then set the value:
sng secret create smtp-password --length 0 --description "Mot de passe SMTP Mailjet"
secret "smtp-password" declared in tenant acme-dev: …
set its value with: sng secret set smtp-password
sng secret set smtp-password --from-file ./smtp-password
As long as the value is not set, the secret exists but is pending: an App that references it does not start that service (see Inject a secret into an App). As soon as you run set, it becomes active; the manifest status follows within 10 minutes.
3. Choose the shape of the generated value
sng secret create api-key --length 40 --contains letters,numbers
| Flag | Role | Default |
|---|---|---|
--length N | length of the generated value; 0 = declared empty | 16 |
--rotation N | renewal every N days; 0 = never; requires --length > 0 | 0 |
--contains | classes: letters, numbers, symbols (aliases letter, alpha, number, digit, symbol, punctuation); requires --length > 0 | all |
--description | free text, visible in sng secret describe | empty |
--dry-run | prints the manifest without applying it | — |
The manifest vocabulary is deliberately simpler than that of sng secret set: no separate lower/upper, no --exclude-chars. If you need these settings, set the value with set (--length 0 in the manifest), or arm the rotation with set --rotation rather than with rotateDays.
4. What is never overwritten
The manifest describes how to produce a value, not the value. Consequences:
- Reapplying the manifest never changes an existing value, nor do the platform's regular checks.
- If you had already run
sng secret set db-passwordbefore declaring the secret, the declaration adopts that value and does not regenerate it. - Changing
lengthorcontainsafterwards does not change the current value: only the first generation and future rotations take it into account. To change the value now:sng secret rotate db-password --length 40. rotateDaysis the only thing that replaces the value, and it does so whatever its origin. Do not putrotateDayson a secret whose value comes from an external service.
5. Make the secret usable by a static site or a notification channel
By default, a secret is only made available to the Apps that reference it. A static site (credentialsSecretRef, tlsSecretRef) or a notification channel (urlSecretRef, webhookSecretRef) also reads a secret of your tenant, but no App references it: declare it with materialize: true.
apiVersion: ssm.stackngo.io/v1alpha1
kind: SSMSecret
metadata:
name: slack-webhook
spec:
length: 0 # the value comes from Slack: you set it with sng secret set
materialize: true # usable by your static sites and notification channels
sng apply secrets/slack-webhook.yml
printf '%s' 'https://hooks.slack.com/services/T000/B000/XXXX' > /tmp/slack-url
sng secret set slack-webhook --from-file /tmp/slack-url
rm /tmp/slack-url
What you get:
- as soon as the secret has a value, it is usable under its name (
slack-webhook) and with its keys (valuefor a single value, your keys for a secret set with--data) by the static sites and notification channels of the tenant, within 10 minutes at most; - each new version, rotation included, reaches them within 5 minutes;
- a secret already set with
sng secret setis adopted the same way: declare anSSMSecretof the same name withlength: 0andmaterialize: true, the existing value is kept.
If another secret of the same name already exists in your tenant (that of a registry robot or of an SSO application, for example), it is never overwritten: the SSMSecret goes to Error with the message the value could not be made available to the other resources of the tenant. Choose another name.
6. Delete
sng kind delete SSMSecret db-password
The manifest is removed from your tenant and the secret is deleted with it, in less than a minute. The secret is no longer listed or readable.
Deletion destroys no version: sng secret restore db-password puts the secret back into service with all its versions, and a later sng secret set db-password recreates it under the same name (see Versions and restore).
sng secret delete db-password on a secret declared by manifest leaves the manifest in place, and it does not become active again on its own. Delete the manifest, or, if you want to keep the secret, restore it with sng secret restore db-password.
Verification
sng kind get SSMSecret db-password # the applied manifest, then its state as comments
sng secret describe db-password # the created secret: version_id ≥ 1, rotation if rotateDays > 0
sng kind get shows the stored manifest, followed by its state (# status:: phase Pending, Active or Error, and the message). sng secret describe confirms that the secret has a value: version_id: "1" (or more) and, where applicable, rotation_enabled, rotation_days, next_rotation_date. A secret declared empty responds version_id: "0" until you run set. If describe responds secret not found several minutes after the apply, the declaration failed: see "If it doesn't work" below.
If it doesn't work
| What you see | Cause | Fix |
|---|---|---|
apply refuses: "rotateDays needs a generated value (length > 0)" | rotateDays on a secret declared empty | Give a length > 0, or remove rotateDays |
apply refuses a length, a rotateDays or a class | Value out of bounds (see below) | Fix the value |
sng secret describe responds version_id: "0" | Secret declared empty, awaiting its value | sng secret set NAME |
secret not found several minutes after the apply | The declaration was refused, often by a tenant policy ("forbidden by the tenant policies") | Check sng tenant policy list; do not contradict the managed policies for secrets |
The secret does not come back after sng secret delete | The secret was deleted without its manifest | sng secret restore NAME, or delete the manifest |
Error: the value could not be made available to the other resources of the tenant | materialize: true and another secret of the same name already exists in the tenant | Rename the SSMSecret |
| Static site or notification channel: secret not found | The secret is not declared with materialize: true, or has no value yet | Declare an SSMSecret with materialize: true, set the value, then give the site or channel time to read it again (up to 15 minutes for a channel) |
Going further
- The platform does not see your values: for a declared secret, it can only have a value generated, never set one of its choosing or read it back. Leave in place the managed policies reserved for secrets (visible in
sng tenant policy list): if one of your policies detaches or contradicts them, yourSSMSecretresources switch toError"forbidden by the tenant policies". - Class vocabulary:
lettersin the manifest becomeslower+upperin the generation policy,numbersbecomesnumber,symbolsbecomessymbol. The value contains at least one character of each selected class. - Validation: at apply time, the API rejects a
rotateDays> 0 withlength: 0("rotateDays needs a generated value (length > 0)"), alengthoutside 0–4096, arotateDaysoutside 0–3650, an unknown class, adescriptionlonger than 1024 characters. - Delays: a pending secret (
Pending) is rechecked every 10 minutes, an active secret (Active) every 6 hours, and one minute after each rotation deadline. - A secret referenced by an App before being declared is created pending automatically (without a manifest). You can then declare it with
SSMSecret: the declaration adopts the existing secret.
See also
- Manifest reference (
SSMSecretpage) - Create and read a secret
- Rotation
- Inject a secret into an App
- The validate, plan, apply cycle
- CLI reference:
sng secret create