Skip to content

TOML Schema

envpkt.toml is validated against a JSON Schema. The schema is published at:

  • npm: envpkt/schema (importable via package exports)
  • GitHub: schemas/envpkt.schema.json

Enable editor autocompletion by adding the schema directive on line 1:

#:schema https://raw.githubusercontent.com/jordanburke/envpkt/main/schemas/envpkt.schema.json

| Field | Type | Required | Description | | --------- | --------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | version | number | Yes | Schema version number (currently 1) | | catalog | string | No | Path to shared secret catalog (relative to config file) | | scope | "shell" | "exec" | No | Whether env export emits this package’s secrets for ambient shell loading (shell) or withholds them so they’re only available via envpkt exec (exec). Default exec. Never affects envpkt exec/env github/env dotenv. |

Optional. Prefixes the injected environment variable name for every [secret.*] and [env.*] entry, without changing the TOML keys you author or the names shown in audit/inspect. The TOML key stays the logical name; the prefix is applied only at the process.env boundary (boot(), exec, env export).

| Field | Type | Required | Description | | ----------- | -------- | -------- | ------------------------------------------------------------------------------------------ | | prefix | string | Yes | Prefix applied to injected names (e.g. CIVCIV__API_KEY) | | separator | string | No | Separator between prefix and key. Default __. Must be shell-safe — see warning below |

[namespace]
prefix = "CIV"
separator = "__" # optional, default
[secret.API_KEY] # injected as CIV__API_KEY
service = "example"
[env.LOG_LEVEL] # injected as CIV__LOG_LEVEL
value = "info"

A logical key keeps its canonical name everywhere internal (audit, from_key references, fnox lookup, catalog merge); only the injected/exported name is prefixed. BootResult.envNames maps each logical key to its wire name.

Identity and capabilities.

| Field | Type | Required | Description | | -------------- | --------------------------------------------- | ------------------------ | ------------------------------------------ | | name | string | Yes (if section present) | Agent display name | | consumer | "agent" \| "service" \| "developer" \| "ci" | No | Consumer type classification | | description | string | No | Agent description or role | | capabilities | string[] | No | Capabilities this agent provides | | expires | string (date) | No | Agent credential expiration (YYYY-MM-DD) | | services | string[] | No | Service dependencies | | key_file | string | No | Path to age identity (private key) file | | recipient | string | No | Agent’s age public key for encryption | | secrets | string[] | No | Secret keys this agent needs from catalog |

Per-secret metadata. Each key corresponds to an environment variable name.

| Field | Type | Required | Tier | Description | | ----------------- | --------------- | -------- | ----------- | ----------------------------------------------------------------------------------- | | service | string | No | Scan-first | Service this secret authenticates to | | expires | string (date) | No | Scan-first | Expiration date (YYYY-MM-DD) | | rotation_url | string (URI) | No | Scan-first | URL for rotation procedure | | purpose | string | No | Context | Why this secret exists | | comment | string | No | Context | Free-form annotation or note | | capabilities | string[] | No | Context | Operations this secret grants | | created | string (date) | No | Context | Provisioning date (YYYY-MM-DD) | | rotates | string | No | Operational | Rotation schedule (e.g., 90d, quarterly) | | rate_limit | string | No | Operational | Rate limit info (e.g., 1000/min) | | model_hint | string | No | Operational | Suggested model or tier | | source | string | No | Operational | Secret origin (e.g., vault, ci, manual) | | encrypted_value | string | No | Value | Age-encrypted ciphertext — mutually exclusive with from_key | | from_key | string | No | Alias | Reference another secret entry (format: "secret.<KEY>") — see Aliases | | required | boolean | No | Enforcement | Whether secret is required for operation | | tags | object | No | Enforcement | Key-value tags for grouping/filtering | | namespace | string | No | Enforcement | Override the file-level [namespace] prefix for this entry. "" opts out entirely |

Plaintext environment defaults. Each key corresponds to an environment variable. These are non-secret values that are safe to commit — use [secret.*] for sensitive credentials.

| Field | Type | Required | Description | | ----------- | -------- | ------------------ | ----------------------------------------------------------------------------- | | value | string | Yes unless aliased | Default value for this env variable — mutually exclusive with from_key | | from_key | string | No | Reference another env entry (format: "env.<KEY>") — see Aliases | | purpose | string | No | Why this env var exists | | comment | string | No | Free-form annotation or note | | tags | object | No | Key-value tags for grouping/filtering | | namespace | string | No | Override the file-level [namespace] prefix for this entry. "" opts out |

[env.NODE_ENV]
value = "production"
purpose = "Runtime environment mode"
comment = "Override to 'development' for local testing"
[env.LOG_LEVEL]
value = "info"
purpose = "Application log verbosity"

Some consumers expect a credential under a different env var name than the one you use canonically. from_key exposes the same governed value under multiple canonical names without duplicating it.

[secret.API_KEY]
service = "example"
expires = "2026-12-31"
rotation_url = "https://api.example.com/settings/keys"
purpose = "Authenticates envpkt-managed tooling"
# Same governed value, exposed under a second name for a consumer
# that hardcodes a different env var name.
[secret.LEGACY_API_KEY]
from_key = "secret.API_KEY"
purpose = "Name required by the legacy consumer"

Works identically for [env.*]:

[env.SERVICE_URL]
value = "https://api.example.com"
[env.LEGACY_URL]
from_key = "env.SERVICE_URL"

Rules:

  • from_key must be formatted as "secret.<KEY>" or "env.<KEY>".
  • The target must exist in the same resolved config (after catalog merge).
  • The target must not itself be an alias — single hop only.
  • An alias cannot also declare a value-producing field (encrypted_value for secrets, value for env entries). It has no value of its own.
  • Cross-type aliasing (secret → env or env → secret) is rejected at load time.

Runtime behavior:

At boot, envpkt injects both the target AND every alias pointing at it, as separate entries in process.env holding the same value:

$ envpkt exec -- env | grep API_KEY
API_KEY=sk-...
LEGACY_API_KEY=sk-...

Audit, envpkt inspect, envpkt env export, and MCP responses all list aliases as first-class entries, tagged with alias_of so the relationship is visible. Status (expiration, health) is inherited from the target — an alias is healthy iff its target is healthy.

envpkt audit --all includes env default drift alongside secret health. Use envpkt audit --env-only to see only env defaults.

Aliases + namespace: a from_key reference is always the logical key ("secret.API_KEY"), independent of any namespace. The alias is injected under its own namespace resolution, so it can bridge a namespaced canonical value to a different injected name — e.g. expose a namespaced secret under a plain, un-prefixed legacy name:

[namespace]
prefix = "CIV"
[secret.API_KEY] # injected as CIV__API_KEY
service = "example"
[secret.LEGACY_API_KEY]
from_key = "secret.API_KEY" # value copied from API_KEY
namespace = "" # injected as plain LEGACY_API_KEY

Policy configuration for credential lifecycle management.

| Field | Type | Default | Description | | -------------------- | --------- | ------- | ---------------------------------------------- | | stale_warning_days | number | 90 | Days since creation to consider a secret stale | | require_expiration | boolean | false | Require expires on all secrets | | require_service | boolean | false | Require service on all secrets |

Automation callbacks for lifecycle events.

| Field | Type | Description | | --------------- | -------- | -------------------------------------------- | | on_expiring | string | Command or webhook when secrets are expiring | | on_expired | string | Command or webhook when secrets have expired | | on_audit_fail | string | Command or webhook on audit failure |

Tool integration configuration. Open namespace for third-party extensions.

[tools]
fnox = true
mcp = true