Skip to content

Latest commit

 

History

History
269 lines (190 loc) · 10.3 KB

File metadata and controls

269 lines (190 loc) · 10.3 KB

Registry Management

The alias registry is the document that maps human-readable alias names to fully-qualified backend URIs. It is the single source of truth for where every secret lives.


What the Registry Is

A flat key-value TOML document stored in any backend you control:

# stored at aws-ssm-platform:///secretenv/registry

stripe-key      = "1password-work://payments/stripe/api_key"
db-url          = "aws-ssm-dev:///myapp/dev/db_url"
datadog-api-key = "1password-work://engineering/datadog/api_key"
redis-url       = "aws-ssm-dev:///myapp/dev/redis_url"
vault-token     = "vault-eng://secret/myapp/vault_token"

Keys are alias names. Values are backend URIs using named instances as the scheme.


Selecting a Registry

Every command resolves which registry to use in this order, the first hit wins, and there is no implicit fallback at the bottom:

1. --registry <name-or-uri>          explicit, per-invocation
2. SECRETENV_REGISTRY=<name-or-uri>  CI / shell-session override
3. [registries.default] in config    machine default
4. hard error                        no assumption is made

The --registry value (and SECRETENV_REGISTRY) is disambiguated by content: if it contains :// it's a direct URI (a single source, no cascade). Otherwise it's a name looked up in [registries.<name>]. SECRETENV_REGISTRY is the canonical mechanism for CI, where no config file lives on the runner.

secretenv run -- npm start                                    # [registries.default]
secretenv run --registry dev -- npm start                     # named registry (may cascade)
secretenv run --registry aws-ssm-dev:///secretenv/reg -- ...  # direct URI, single source

Cascading Registries

A registry configuration can cascade across multiple source documents. Sources are checked in order, first match wins. Entries in later sources that share a key with an earlier source are silently shadowed.

# config.toml

[registries.dev]
sources = [
  "aws-ssm-dev:///secretenv/dev-registry",       # team-specific, checked first
  "aws-ssm-platform:///secretenv/org-registry",  # org-wide fallback
]

Use cases for cascading:

  • Team-specific aliases that override org-wide defaults
  • Per-environment aliases alongside shared infrastructure aliases
  • Gradual migration from one registry to another

CLI Commands

All registry commands accept --registry <name-or-uri> to target a specific registry. Without it, the active registry (from --registry flag, SECRETENV_REGISTRY, or [registries.default]) is used.

registry list

Shows all aliases across all sources in the active registry, with provenance and shadowing clearly marked.

secretenv registry list
secretenv registry list --registry dev
Registry: dev  (2 sources)
─────────────────────────────────────────────────────────────────────

aws-ssm-dev:///secretenv/dev-registry                    [source 1]
┌─────────────────────────────────────────────────────────────────┐
│  stripe-key       →  aws-ssm-dev:///myapp/dev/stripe_key       │
│  db-url           →  aws-ssm-dev:///myapp/dev/db_url           │
│  redis-url        →  aws-ssm-dev:///myapp/dev/redis_url        │
└─────────────────────────────────────────────────────────────────┘

aws-ssm-platform:///secretenv/org-registry               [source 2]
┌─────────────────────────────────────────────────────────────────┐
│  stripe-key       →  1password-work://payments/stripe   ↑ shadowed by source 1
│  datadog-api-key  →  1password-work://engineering/datadog       │
│  vault-token      →  vault-eng://secret/myapp/vault_token       │
└─────────────────────────────────────────────────────────────────┘

Resolved aliases: 5  (1 shadowed)

registry get

Shows what a single alias resolves to, including which source it came from and what it shadows.

secretenv registry get stripe-key
secretenv registry get stripe-key --registry dev
stripe-key
  resolved by:  aws-ssm-dev:///secretenv/dev-registry  [source 1]
  points to:    aws-ssm-dev:///myapp/dev/stripe_key
  shadowing:    1password-work://payments/stripe  (source 2)

registry set

Writes an alias to the registry. Always writes to source[0] of the active registry.

secretenv registry set stripe-key "1password-work://payments/stripe/api_key"
secretenv registry set db-url "aws-ssm-prod:///myapp/prod/db_url" --registry prod
✓ Written to source 1: aws-ssm-dev:///secretenv/dev-registry
  stripe-key  →  1password-work://payments/stripe/api_key

To write to a specific source, pass a direct URI:

secretenv registry set stripe-key "..." \
  --registry aws-ssm-platform:///secretenv/org-registry

Validation: the value must be a valid backend URI (contains :// with a recognized scheme). Hard error otherwise.

registry unset

Removes an alias from source[0] of the active registry.

secretenv registry unset old-deprecated-key
secretenv registry unset old-key --registry dev
✓ Removed from aws-ssm-dev:///secretenv/dev-registry
  old-key  (was → aws-ssm-dev:///myapp/dev/old_key)

If the alias only exists in a downstream source, secretenv warns rather than silently doing nothing:

⚠ 'datadog-api-key' not found in source 1 (aws-ssm-dev:///secretenv/dev-registry)
  found in source 2 (aws-ssm-platform:///secretenv/org-registry)
  to remove from source 2, pass it explicitly:
  secretenv registry unset datadog-api-key \
    --registry aws-ssm-platform:///secretenv/org-registry

registry history

Shows version history of the secret an alias resolves to, where the backend supports it. Output is most-recent-first.

secretenv registry history <alias>
secretenv registry history <alias> --registry prod
secretenv registry history <alias> --json
alias:    stripe-key
resolved: vault-eng:///secret/payments/stripe

  v3  2026-04-10  ...
  v2  2026-03-20  ...
  v1  2026-03-01  ...

--json emits machine-readable output. Backends with no native history API return an error naming the backend type so the CLI can distinguish unsupported from a real failure.

Backend support:

Backend History support
AWS SSM SecureString ✓ Parameter versions via SSM API
HashiCorp Vault KV v2 ✓ Native versioning
Local file ✓ git log (requires the file to be under a git repo)
1Password ✗ Not implemented
AWS Secrets Manager ✗ Not implemented
All other backends ✗ Not implemented

registry invite

Generates the onboarding command for a new team member based on the active registry configuration.

secretenv registry invite
Share this with new team members:

secretenv setup aws-ssm-platform:///secretenv/org-registry

Or with a distribution profile (pre-configures all backends and registries):
curl -sfS https://secretenv.io/install.sh | sh -s -- --profile acme-corp

Writing the Registry Document Manually

For teams managing infrastructure as code, the registry document can be managed via Terraform or any tool that can write to a backend. secretenv does not require using the CLI to manage registry content; it only needs to be able to read the document at runtime.

The registry document format is a flat TOML key-value structure:

alias-name = "backend-instance://path/to/secret"

Via Terraform (AWS SSM):

resource "aws_ssm_parameter" "secretenv_registry" {
  name  = "/secretenv/org-registry"
  type  = "SecureString"   # always SecureString, never String
  value = jsonencode({
    "stripe-key"      = "1password-work://payments/stripe/api_key"
    "db-url"          = "aws-ssm-prod:///myapp/prod/db_url"
    "datadog-api-key" = "1password-work://engineering/datadog/api_key"
  })
}

Note: when managing the registry via Terraform, the alias-to-path mappings are in version-controlled Terraform state. This is an operational choice: the alias values (backend paths) are organizational configuration, not secret values, so this tradeoff is similar to managing Kubernetes ExternalSecrets manifests in code.


Registry Access Controls

The registry document maps alias names to backend paths. It does not contain secret values. However its access controls matter.

Treat the registry's access controls equivalently to your most sensitive secret.

If an attacker can read the registry, they learn your secrets topology: which backends you use, what paths your secrets live at, your naming conventions. They do not get secret values. But if they already have authenticated access to the registry backend, they likely have broader backend access anyway.

Recommended storage:

Backend Recommended configuration
AWS SSM SecureString type with KMS encryption. IAM policy: ssm:GetParameter scoped to the registry path only, for the identities that need it.
HashiCorp Vault KV v2 with a policy that grants read access to the registry path. Separate policy from application secret access.
1Password Dedicated vault for secretenv registry. Share only with team leads and platform engineers, not all developers.
Local file chmod 600. For solo developers only.

Alias Naming Conventions

No convention is enforced by secretenv. Recommendations:

  • Use kebab-case: stripe-key, prod-db-url, datadog-api-key
  • Keep alias names environment-agnostic where possible. Route environments via registry selection, not alias naming
  • Use consistent naming across services. If multiple services need a shared Datadog key, one alias shared via the org registry is better than service-a-datadog-key and service-b-datadog-key
  • Prefix team-specific aliases if co-existing with an org registry: payments-stripe-key vs org-level stripe-key