Skip to content

Reference

alter managed-secrets

Manage backend credentials, their grants, and effective access.

Managed secrets are backend-only credentials (API keys, signing secrets) that Alter stores and injects into outbound requests — no OAuth user involved. These commands cover the full lifecycle: the secrets themselves, the grants that hand them to principals, and the helpers that answer “who can reach this secret.”

alter managed-secrets templates
alter managed-secrets list
alter managed-secrets show <secret-id>
alter managed-secrets create (--input <path> | --name <name> (--credential-value <value> | --credentials <@file.json>)) [...]
alter managed-secrets rotate <secret-id> (--input <path> | --credential-value <value> | --credentials <@file.json>) [--template <template-id>] [--skip-preflight] [--confirm-template-migration <slug>]
alter managed-secrets delete <secret-id> [--confirm <slug>]
alter managed-secrets access <secret-id>
alter managed-secrets set-delegation-policy <secret-id> [--allow-group-delegation] [--max-delegation-ttl-days <days>] [--delegable] [--max-delegation-depth <n>]
alter managed-secrets set-allowed-hosts <secret-id> --host <host> [--host ...] | --clear
alter managed-secrets users
alter managed-secrets groups list | show <group-id>
alter managed-secrets grants list | list-for-agent | create | update | revoke

All commands are app-scoped — pass --app <id> (or the app’s name), or link a workspace / set ALTER_APP_ID. Reads need dashboard_secrets:read; create / rotate / set-delegation-policy / set-allowed-hosts / grant create + update need dashboard_secrets:write; grants revoke needs dashboard_secrets:admin (the revoke tier, matching grants revoke); delete needs dashboard_secrets:delete.

List the managed-secret template catalog for the app (openai, aws, …). Templates pre-fill the credential type and injection rules.

Terminal window
alter managed-secrets templates

--limit accepts 1–1000 (default 200); --offset defaults to 0.

Terminal window
alter managed-secrets list
alter managed-secrets show <secret-id> # never exposes the credential value

list accepts --limit 1–1000 (default 100) and --offset (default 0).

Create a managed secret and its base grant. --name is required unless you supply a full --input body.

The slug is server-generated from --name — the name is slugified (e.g. Stripe Productionstripe-production), with a -<N> suffix appended only if that slug is already taken in the app. Name the secret after the provider so the slug is meaningful; the slug is the stable machine handle the SDK, CLI, dashboard, and audit logs reference. A stale request body that still ships a slug field is silently ignored.

FlagDescription
--name <name>Human display name. Required in per-field mode; omitted when --input supplies the body.
--description <text>Free-form description.
--template <id>Template ID (e.g. openai); omit for a custom secret.
--credential-type <type>bearer_token (default), header_key, basic_auth, or aws_sig_v4.
--credential-value <value>Primary credential. Required in per-field mode unless --credentials contains credential_value. See credential intake.
--credentials <@file.json>Multi-field credentials as a JSON object — preferred for multi-field types.
--credential-field <k=v>Repeatable; sets one additional credential field per flag.
--injection-header <name>Header to inject into (default Authorization).
--injection-format <template>Header value template (default Bearer {token}).
--injection-rule <@file.json>Path-prefixed JSON array of up to 10 additional injection rules; see the shape below.
--label <label>Base-grant display label.
--account-identifier <id>Optional account identifier metadata stored on the secret.
--account-display-name <name>Optional account display name metadata.
--allow-group-delegationAllow members of a group-typed grant to delegate this secret to an agent (default: off).
--max-delegation-ttl-days <days>Cap on a single delegation’s lifetime, 1–1825 days (default 90).
--delegableDeveloper ceiling: allow grants under this secret to be marked re-delegable, gating onward agent → agent delegation for the whole secret (default: off).
--max-delegation-depth <n>Max delegation-chain depth under this secret, 0–8 (global cap is 8).
--skip-preflightSave without the provider credential check (audited; use only when the provider endpoint is unavailable). The response’s preflight summary reports skipped so the unverified state is never silent.
--input <path>JSON body from a file (@path) or stdin (-); replaces the per-field flags.
Terminal window
# Single-field bearer token, value piped from stdin
printf '%s' "$OPENAI_KEY" | alter managed-secrets create \
--name "OpenAI" --template openai --credential-value -

Supabase exposes separate project and account-management contracts. Use a project key (sb_publishable_, sb_secret_, or a legacy project JWT) with the Project APIs template and supply its exact Project URL:

Terminal window
alter managed-secrets create \
--name "Supabase Project APIs" \
--template supabase \
--credential-value @./supabase-project-key \
--credential-field project_url=https://PROJECT_REF.supabase.co

Use an sbp_ personal access token with the Management API template:

Terminal window
alter managed-secrets create \
--name "Supabase Management" \
--template supabase-management \
--credential-value @./supabase-management-pat

The CLI and backend reject unknown or cross-product credential shapes. Both templates own their injection contract, so --injection-rule, additional_injections, custom injection headers, and custom injection formats cannot override it. Project credentials are bound to exactly PROJECT_REF.supabase.co and Supabase’s documented /rest/v1, /auth/v1, /storage/v1, /functions/v1, /realtime/v1, and /graphql/v1 surfaces; Management credentials are bound to exactly api.supabase.com. set-allowed-hosts cannot widen or clear either binding.

Rotating a historical supabase row with an sbp_ PAT can atomically reclassify it as supabase-management, remove the project binding, and repoint every existing grant. This one-way action requires --confirm-template-migration <slug>, where <slug> exactly matches the target secret. An --input body may instead carry the same slug in the backend wire field "confirm_template_migration"; if both forms are supplied, they must match. Current v2 Project API secrets cannot be converted in place; create a separate Management secret. The CLI normally reads the current template to mirror dashboard validation. Passing --template skips that lookup for ordinary rotations; a Data-template PAT migration still reads the target so it can verify the slug and eligibility before the mutating request. The server independently rechecks the exact slug, template, contract version, and credential class before storing anything and again under the mutation lock.

Where the template supports it, the credential is verified against the provider at save time, and the CLI prints the verdict on stderr — exactly what the dashboard shows inline:

  • passed — the provider accepted the credential.
  • inconclusive — verification couldn’t complete (for example a transient network issue). The save proceeds; verify the key with a test call.
  • skipped — the credential type can’t be probed. The save proceeds unverified; the message names why.

A credential the provider definitively rejects fails the save with a structured error and nothing is stored. The same verdicts print on rotate.

The primary credential value (--credential-value) accepts three forms, in decreasing order of safety:

FormReads from
--credential-value -All stdin through EOF (preferred for CI). Surrounding whitespace is trimmed; embedded CR, LF, or null characters are rejected.
--credential-value @/path/to/fileA file.
--credential-value <value>Inline — prints a shell-history warning.

Multi-field credential types (such as aws_sig_v4, which needs an access key, secret key, and region) take either --credentials @file.json (a JSON object of string fields — it may also include a top-level credential_value to satisfy the primary value in one file) or repeated --credential-field key=value flags. If both are supplied, the file wins and the flags are ignored with a warning.

The file form also reads the complete file and trims surrounding whitespace; embedded CR, LF, or null characters are rejected in every primary-credential input form.

Each --injection-rule entry is {"target":"header|query_param","key":"<name>","value_source":"token|additional_credentials.<snake_case_field>"}. key must start with a letter, contain only letters, digits, -, or _, and be 1–100 characters. The file must begin with @, contain a JSON array, and hold at most 10 entries.

Provider templates may make injection rules immutable. Supabase Project APIs and Management are header-only provider contracts and reject additional injection rules, including query-parameter injection.

Swap the stored credential while active grants continue to use the same managed secret. Same credential-intake forms and precedence as create: provide --credential-value, a credential_value in --credentials, or an --input body containing credential_value.

The CLI reads the secret’s current template to mirror the dashboard’s provider-specific checks before sending the rotation. dashboard_secrets:write already implies read access. Pass --template <template-id> (or expected_template_id in --input) when you want to avoid that lookup and make the current provider an explicit optimistic precondition. With --input, --template is an allowed sideband precondition only when the JSON body does not contain expected_template_id; supplying both is rejected instead of silently weakening either value. The server verifies the precondition atomically; a concurrent provider change returns conflict instead of applying validation for the wrong provider.

Pass --skip-preflight only when the provider verification endpoint is unavailable. It is an audited query-side escape hatch in both per-field and --input modes; the credential body is unchanged, and the response reports the preflight as skipped so the unverified rotation is explicit.

--confirm-template-migration <slug> is only for the historical Supabase Project APIs → Management API repair described above. The exact slug binds confirmation to one target and prevents a reused rotation command from reclassifying a different secret. It does not weaken the server’s template, contract-version, or credential-class checks.

Terminal window
printf '%s' "$NEW_KEY" | alter managed-secrets rotate <secret-id> --credential-value -

Like apps delete, it’s gated behind the separate dashboard_secrets:delete scope and a type-to-confirm check against the secret’s slug:

  • Interactive: the CLI prompts for the slug. This path also needs dashboard_secrets:read — it fetches the secret to show the slug in the prompt.
  • Non-interactive (CI): pass --confirm <slug>. A dashboard_secrets:delete-only PAT (no :read) must use this form.

The explicit form is forwarded to the server without a read. A wrong slug is therefore rejected by the server as a generic server error (exit code 1), rather than as a local usage error.

Terminal window
alter managed-secrets delete <secret-id> --confirm openai-api

Compute the effective access for a secret — every principal that can reach it, whether directly, through a group, as a system principal, or via an agent. Use --output json to keep the full nested envelope with per-path counts.

--limit accepts 1–1000 (default 200); --offset defaults to 0.

Terminal window
alter managed-secrets access <secret-id> --output json

Set the user → agent delegation policy on a managed secret — controls whether members of a group-typed grant may delegate the secret to an agent, caps a delegation’s lifetime, and sets the operator ceiling that gates onward agent → agent delegation chains. Needs dashboard_secrets:write.

FlagDescription
--allow-group-delegationAllow members of a group-typed grant to delegate this secret to an agent (default: off).
--max-delegation-ttl-days <days>Cap on a single delegation’s lifetime, 1–1825 days (default 90).
--delegableDeveloper ceiling: allow grants under this secret to be marked re-delegable, gating onward agent → agent delegation for the whole secret (default: off).
--max-delegation-depth <n>Max delegation-chain depth under this secret, 0–8 (global cap is 8).
Terminal window
# Enable group delegation with a 30-day cap
alter managed-secrets set-delegation-policy <secret-id> --allow-group-delegation --max-delegation-ttl-days 30
# Allow onward agent → agent delegation up to 3 hops deep
alter managed-secrets set-delegation-policy <secret-id> --delegable --max-delegation-depth 3
# Turn group delegation back off (omit the flag)
alter managed-secrets set-delegation-policy <secret-id>

Restrict which destination hosts a secret may be injected into when proxied — an egress allowlist that bounds where the credential can be sent. Needs dashboard_secrets:write.

FlagDescription
--host <host>Allowed host: exact (api.stripe.com) or leading-wildcard subdomain (*.stripe.com). Repeatable, or comma-separated; both forms accumulate.
--clearRemove all configured allowed hosts. With default enforcement, proxy calls for the secret are blocked until hosts are configured again. Mutually exclusive with --host.
Terminal window
# Restrict to Stripe only
alter managed-secrets set-allowed-hosts <secret-id> --host api.stripe.com --host '*.stripe.com'
# Clear all allowed hosts (proxy calls are blocked until hosts are configured)
alter managed-secrets set-allowed-hosts <secret-id> --clear

Picker helpers for building grants — list the app’s users and groups, with substring search.

Terminal window
alter managed-secrets users --search alice
alter managed-secrets groups list --idp <idp-id>
alter managed-secrets groups show <group-id>

users and groups list accept --idp <uuid>, --search, --limit (1–200, default 50), and --offset (default 0). groups show paginates members and grants independently with --member-limit / --member-offset and --grant-limit / --grant-offset; both limits default to 100 and accept 1–500.

Grants hand a secret to a principal. Revoking a grant is a soft-delete (recoverable by re-granting). Create and update need dashboard_secrets:write; revoke needs dashboard_secrets:admin — the same recoverable-revoke tier as API keys, agents, and OAuth grants, so a routine :write CI token can’t cut off live principals.

Both grant list commands accept --limit 1–1000 (default 100) and --offset (default 0).

alter managed-secrets grants list <secret-id>
alter managed-secrets grants list-for-agent <agent-id>
alter managed-secrets grants create <secret-id> --principal-type <type> [...]
alter managed-secrets grants update <grant-id> [--label ...] [--expires-at ...]
alter managed-secrets grants revoke <grant-id> [--yes]

The principal type drives which flags are required:

--principal-typeRequired flags
user--app-user-id <uuid>, --label <string>
group--idp-id <uuid>, --external-group-id <id>, --label <string>
agent--agent-id <uuid>
system

Optional on every type: --description, --expires-at <iso>, and --grant-policy <@file.json> for the full policy shape. The policy flag requires a path-prefixed JSON object; when both policy input and --expires-at specify an expiry, --expires-at wins. Or supply the whole body with --input.

Terminal window
alter managed-secrets grants create <secret-id> \
--principal-type agent --agent-id <agent-id> --expires-at 2026-12-31T00:00:00Z

--external-group-id takes the IDP’s stable group identifier, never a display name — an Okta group id (00g…), or a Clerk / WorkOS organization id (org_…):

Terminal window
# Okta group
alter managed-secrets grants create <secret-id> \
--principal-type group --idp-id <idp-id> \
--external-group-id 00g8xk2mnpQRS7Vw1697 --label "Data science team"
# WorkOS organization
alter managed-secrets grants create <secret-id> \
--principal-type group --idp-id <idp-id> \
--external-group-id org_01HXYZABCDEF123456789 --label "Acme Corp"

When a grant policy names human-in-the-loop approvers (--grant-policy on grants create / grants update, or a grant_policy in a create --input body), the backend validates each approver email against the identity provider’s directory at save time:

  • An approver the directory definitively does not contain fails the save with a structured 422 — fix the email and retry.
  • An approver the directory couldn’t verify (no management credential configured, a transient directory error) allows the save and prints a warning on stderr: alter: approver not verified — … (save was allowed). Confirm the address is correct — an unreachable approver silently stalls every approval it gates.

Edit --label, --description, --expires-at, or --grant-policy (at least one required), or supply --input.

Terminal window
alter managed-secrets grants update <grant-id> --label "renamed"
Terminal window
alter managed-secrets grants revoke <grant-id> # prompts y/N
alter managed-secrets grants revoke <grant-id> --yes # CI

Table output prints Revoked grant <id>.; JSON and JSONL output emit {"grant_id":"<id>","status":"revoked"}.

Provision a backend credential in CI

Terminal window
printf '%s' "$OPENAI_KEY" | alter managed-secrets create \
--name "OpenAI" --template openai --credential-value -

Grant an agent access, then verify it resolved

Terminal window
alter managed-secrets grants create <secret-id> --principal-type agent --agent-id <agent-id>
alter managed-secrets access <secret-id> --output json | jq '.entries'

Report an issue with this page

Necessary

Required for sign-in, security, authorization, and remembering your choices.

Always active

Analytics

Helps us understand which product and documentation features are useful.

Performance diagnostics

Uses performance tracing and privacy-masked session replay to diagnose problems.

You can change these choices at any time from Cookie settings.