Platform
Named variables
Map a stable name such as LOG_SERVER_TX1_S to one field of an existing credential, then resolve %%LOG_SERVER_TX1_S%% when a playbook, application or build needs it. Assignments hold references, never copied values, so rotation changes the next resolved value without editing templates.
Create an assignment
Use Secret Variables in the dashboard, the desktop app's Variables tab, an SDK, Terraform or the CLI:
# A root_credential stores its password under the password field ss variables assign LOG_SERVER_TX1_S root_credential CREDENTIAL_UUID password # A password record uses the value field ss variables assign LOG_SERVER_TX1_S password PASSWORD_UUID value ss variables list ss variables delete LOG_SERVER_TX1_S
- Managing assignments needs
variables:readandvariables:write. The built-in member, user and developer roles have both, as do admins. - The credential must exist and belong to the same account.
- The field is a literal top-level key of the stored credential's data, not a JSONPath expression. Field names match
[A-Za-z_][A-Za-z0-9_.-]{0,127}. - Assignment IDs stay the same across updates; deleting and recreating an assignment gives it a new ID.
Supported target types: secret, password, api_token, certificate, ssh_key, gpg_key, openssl_key, ntlm_hash, computer_credential, wifi_credential, windows_credential, social_credential, disk_credential, service_config_credential, root_credential, ldap_bind_credential, integration_credential and code_signing_key.
Resolution rules
| Rule | Detail |
|---|---|
| Names | [A-Z_][A-Z0-9_]{0,127}, case-sensitive, unique per account |
| Token | %%NAME%% resolves the current string field. Empty strings are valid. |
| Escape | %%%% is a literal %%, so %%%%NAME%%%% becomes %%NAME%% |
| Single pass | Inserted values are never scanned for more tokens |
| All or nothing | A missing variable (404), denied permission (403), expired secret or inactive API token (410), missing or non-string field (422) or size limit (413) fails the whole request. No partial output is returned. |
| Consistency | Repeated references to one name use one value per request. Different variables are not read as an atomic snapshot. |
| Limits | 1 MiB request, 4 MiB response, 256 distinct names, 64 levels of document nesting |
| Privacy | Responses carry Cache-Control: no-store. Audit events record the assignment and credential reference, never templates or values. |
Resolving also needs read access to each underlying credential, and key types additionally need export:read. Knowing a variable name grants no access.
REST API
| Method | Endpoint | Result |
|---|---|---|
| PUT | /api/v1/variables/NAME | Assign or update {secret_type, secret_id, field} |
| GET | /api/v1/variables/NAME | Assignment metadata |
| GET | /api/v1/variables | {variables: [...]}, up to 1,000 assignments |
| DELETE | /api/v1/variables/NAME | Delete the assignment; 204 |
| POST | /api/v1/variables/resolve | Resolve text or a JSON document |
Text
POST /api/v1/variables/resolve
{"template": "sudo_password=%%LOG_SERVER_TX1_S%%"}
{"rendered": "sudo_password=...", "variables": ["LOG_SERVER_TX1_S"]}JSON document
POST /api/v1/variables/resolve
{"document": {"password": "%%LOG_SERVER_TX1_S%%", "port": 443}}The document response has document and variables. Only string values are processed; keys, numbers, booleans, null and structure are preserved, and quotes or newlines in values are encoded as JSON data.
Clients and tools
| Consumer | Assign | Resolve text | Resolve JSON |
|---|---|---|---|
| Python | assign_variable | render(text) | resolve_document(doc) |
| Node.js / TypeScript | assignVariable | render(text) | resolveDocument(doc) |
| PHP | assignVariable | render(text) | resolveDocument(doc) |
| ss CLI | ss variables assign | ss render FILE | ss render --json FILE |
| Device agent | Admin grants in a profile | secretserver-agent render --input FILE | add --json |
| Desktop app | Variables tab | Resolve template button | Use an SDK |
Ansible
vars:
ansible_become_password: "{{ lookup('secretserver', '%%LOG_SERVER_TX1_S%%') }}"Supply SS_API_KEY and SS_API_URL or the lookup's api_key and api_url options, and set no_log: true on tasks that use the result. render=true treats a term as a template even without tokens. Placing %%NAME%% in ordinary YAML does nothing; the lookup must be invoked.
Terraform
resource "secretserver_variable" "sudo" {
name = "LOG_SERVER_TX1_S"
secret_type = "root_credential"
secret_id = var.root_credential_id
field = "password"
}
ephemeral "secretserver_template" "sudo" {
template = "%%LOG_SERVER_TX1_S%%"
depends_on = [secretserver_variable.sudo]
}The resource stores only assignment metadata in state. Use the ephemeral value only where Terraform allows ephemeral values (Terraform 1.10 or newer). There is deliberately no ordinary template data source.
Device agent grants
{"alias": "sudo-password", "service": "variable.resolve", "resource": "VARIABLE_UUID"}A device resolves only the variable IDs granted to its profile, through signed POST /api/v1/agent/variables/resolve requests. secretserver-agent run writes each such grant to a private <alias>.json file in its output directory.
MCP bridge
Resolution is off by default. SECRETSERVER_ENABLE_SECRET_RESOLUTION=1 together with an allowlist in SECRETSERVER_RESOLVE_ALLOW=NAME[,NAME...] registers resolve_secret_template, whose plaintext result enters model context. The bridge refuses to start with the switch on and no allowlist.
Rendered files
umask 077 ss render --json application.template.json > application.private.json # or with an enrolled device identity secretserver-agent render --json --input application.template.json > application.private.json
Rendered output contains plaintext secrets. Keep it out of source control, build logs, caches and published artifacts, and prefer runtime injection for services. A failed command emits nothing, but shell redirection may already have truncated the destination; render to a temporary file and rename it on success when replacing live configuration.