Skip to main content

VAULTS

A vault is a named secret storage backend. Vaults store and retrieve sensitive values — API keys, passwords, tokens — used in model definitions and workflows. Vault configurations live in vaults/{vault-type}/{id}.yaml within a swamp repository. Encrypted secret data lives in .swamp/secrets/.

File Structure

vaults/
  local_encryption/
    0dd4c691-0b7b-4fae-9741-31e96a4efc69.yaml

.swamp/secrets/
  local_encryption/
    my-secrets/
      .key
      api-key.enc
      db-password.enc

The vaults/ directory stores vault configurations (tracked in git). The .swamp/secrets/ directory stores encrypted secret data (not tracked in git).

Vault Configuration

Each vault is defined by a YAML file with the following fields:

id: 0dd4c691-0b7b-4fae-9741-31e96a4efc69
name: my-secrets
type: local_encryption
config:
  auto_generate: true
  base_dir: /home/user/my-repo
createdAt: "2026-04-07T16:59:59.039Z"

id

Unique identifier for the vault configuration.

Property Value
Type string (UUID v4)
Required Yes
Default Auto-generated on create
Format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

name

Human-readable name used to reference the vault in CLI commands and CEL expressions.

Property Value
Type string
Required Yes
Default None

Must be unique within the repository. Must start with a lowercase letter and contain only lowercase letters, numbers, and hyphens.

type

Vault provider type.

Property Value
Type string
Required Yes
Default None

See Vault Types for available values.

config

Provider-specific configuration.

Property Value
Type Record<string, unknown>
Required No
Default {}

Available keys depend on the vault type. See the configuration section for each type below.

createdAt

ISO 8601 timestamp of when the vault was created.

Property Value
Type string
Required Yes
Default Set on create
Format ISO 8601 (Z)

Vault Types

local_encryption

Stores encrypted secrets in local files using AES-GCM encryption. This is the built-in vault type available in every swamp repository.

Configuration

Key Type Required Default Description
ssh_key_path string No ~/.ssh/id_rsa Path to an unencrypted SSH private key
auto_generate boolean No true Auto-generate an encryption key if no SSH key
key_file string No secrets/.key Custom path for the auto-generated key file
base_dir string No Repository root Base directory for secret storage (auto-injected)

When no ssh_key_path is provided and auto_generate is true (the default), swamp creates a random 72-character key (two concatenated UUIDs) at .swamp/secrets/local_encryption/{vault-name}/.key.

When ssh_key_path is provided, the SSH private key is used as key material for PBKDF2 derivation. The key must be unencrypted — passphrase-protected SSH keys (both legacy PEM and OpenSSH format) are rejected.

# Auto-generated key (default)
$ swamp vault create local_encryption my-secrets

# SSH key-backed
$ swamp vault create local_encryption infra \
    --config '{"ssh_key_path":"~/.ssh/id_ed25519"}'

Encryption Details

Property Value
Algorithm AES-GCM (authenticated encryption)
Key size 256-bit
IV size 96-bit (random per secret)
Salt size 128-bit (random per secret)
Key derivation PBKDF2 with SHA-256, 100,000 iterations

Each encrypted file is stored as JSON:

{
  "iv": "ZhURGq+UA75P0Ydb",
  "data": "OQayYKhVm6AcQXpzukNcSueagt1b3OQvtxg9it8=",
  "salt": "sCvS1h08k+lFZ37PIfjSLQ==",
  "version": 2
}

All values are base64-encoded. version tracks the encrypted data format.

File Permissions

Path Mode
.swamp/secrets/local_encryption/{vault-name}/ 700
.swamp/secrets/local_encryption/{vault-name}/.key 600
.swamp/secrets/local_encryption/{vault-name}/{key}.enc 600

SSH key files are validated for 600 or stricter permissions on Unix systems.

Extension Vault Types

Additional vault types are available through extensions:

Type Extension Description
@swamp/aws-sm @swamp/aws-sm AWS Secrets Manager
@swamp/azure-kv @swamp/azure-kv Azure Key Vault
@swamp/1password @swamp/1password 1Password integration

Legacy type names (aws, aws-sm, azure, azure-kv, 1password) are automatically mapped to their scoped equivalents.

Custom Vault Types

Custom vault providers can be added via extension files in extensions/vaults/*.ts. Each file must export a vault object implementing the provider interface. See the Create and Publish an Extension guide for packaging extensions.

Custom vault type names must use the @collective/name format. Reserved collectives (swamp, si) are not allowed.

$ swamp vault type search
{
  "query": "",
  "results": [
    {
      "type": "local_encryption",
      "name": "Local Encryption",
      "description": "Store encrypted secrets in local files using AES-GCM encryption. Uses SSH private key or auto-generated key for encryption."
    }
  ]
}

Key Storage and Retrieval

Storing Secrets

$ swamp vault put my-secrets api-key sk-test-12345
17:00:04 INF vault·put Stored secret "api-key" in vault "my-secrets"

The value can be provided in several ways:

Method Command
Third argument swamp vault put <vault> <key> <value>
Inline swamp vault put <vault> KEY=VALUE
Piped stdin echo "$SECRET" | swamp vault put <vault> <key>
Interactive swamp vault put <vault> <key> (hidden prompt)

Use --force (-f) to overwrite an existing key without confirmation.

Piping via stdin is recommended for scripts and CI to avoid exposing secrets in the process argument list.

Retrieving Secrets

Secrets are retrieved through CEL expressions in model definitions and workflows, not through a CLI read command. See CEL Integration below.

Listing Keys

$ swamp vault list-keys my-secrets
17:00:29 INF vault·list-keys Vault "my-secrets" ("local_encryption"): 2 key(s)
17:00:29 INF vault·list-keys   - "api-key"
17:00:29 INF vault·list-keys   - "db-password"

Lists key names only. Values are never displayed.


CEL Integration

Vault secrets are accessed in CEL expressions using vault.get() and vault.put().

vault.get(vault_name, key)

Retrieves a secret value from the named vault.

globalArguments:
  secret: "${{ vault.get('my-secrets', 'api-key') }}"

Supported quoting styles for vault name and key arguments:

# Single quotes
secret: "${{ vault.get('my-secrets', 'api-key') }}"

# Double quotes
secret: "${{ vault.get(\"my-secrets\", \"api-key\") }}"

# Backticks
secret: "${{ vault.get(`my-secrets`, `api-key`) }}"

# Unquoted (when names contain only alphanumeric characters and hyphens)
secret: "${{ vault.get(my-secrets, api-key) }}"

vault.put(vault_name, key, value)

Stores a value in the named vault. Used in expressions where a computed value needs to be persisted as a secret.

globalArguments:
  store: "${{ vault.put('my-secrets', 'generated-key', self.data.attributes.token) }}"

Expression Context

vault.get() expressions are deferred to runtime — they are not evaluated during definition validation or persistence. This means the vault and key must exist when the model method or workflow step executes.

The vault context variable is available in:

See the CEL Expressions reference for the full expression language.


Environment Variable Mounting

When a shell command contains vault.get() expressions, swamp resolves the secrets and passes them to the executing process as environment variables. The actual secret values never appear in the command string.

Resolution Process

  1. During expression evaluation, each vault.get() call is replaced with an internal sentinel token.
  2. Before shell execution, each sentinel is replaced with a shell variable reference (${__SWAMP_VAULT_N}), where N is a sequential index.
  3. The raw secret values are passed as environment variables to the process.
  4. The shell expands the variables after command parsing, so metacharacters in secret values are never interpreted as shell syntax.

Quoting Behavior

Sentinel replacement is quoting-aware:

Context Replacement
Inside double quotes ${__SWAMP_VAULT_N}
Outside double quotes "${__SWAMP_VAULT_N}"

For example, given a model argument:

methods:
  execute:
    arguments:
      run: "curl -H \"Authorization: Bearer ${{ vault.get('my-secrets', 'api-key') }}\" https://api.example.com"

The shell receives:

curl -H "Authorization: Bearer ${__SWAMP_VAULT_0}" https://api.example.com
# with env: __SWAMP_VAULT_0=sk-test-12345

A secret containing shell metacharacters like pass;rm -rf / is treated as literal data because variable expansion happens after command parsing.


Sensitive Field Processing

Model types can mark resource output fields as sensitive. When a method produces sensitive output, the values are automatically stored in a vault and replaced with vault.get() reference expressions before persistence.

Vault Resolution Order

The vault used for storing sensitive fields is resolved in this order:

  1. Field-level vaultName from schema metadata
  2. Spec-level vaultName from the resource output specification
  3. First available vault from the vault service

If sensitive fields exist but no vault is configured, an error is thrown.

Auto-Generated Vault Keys

When no custom vault key is specified, the key is generated from the model context:

{sanitized-model-type}-{model-id}-{method-name}-{field-path}

Sanitization rules: @ is removed, / and \ are replaced with -.

Example: model type @user/aws/ec2-keypair, field KeyMaterial becomes user-aws-ec2-keypair-{id}-createKeyPair-KeyMaterial.

Persisted Format

After processing, the persisted data contains vault references instead of raw values:

keyMaterial: "${{ vault.get('my-secrets', 'user-aws-ec2-keypair-abc-createKeyPair-KeyMaterial') }}"

Non-string values are JSON-stringified before vault storage.


CLI Commands

swamp vault create <type> [name]

Create a new vault configuration.

Option Description
--config Provider configuration as JSON
--repo-dir Repository directory (default .)
$ swamp vault create local_encryption my-secrets
17:00:00 INF vault·create Created vault: "my-secrets" ("Local Encryption")
$ swamp vault create local_encryption infra \
    --config '{"ssh_key_path":"~/.ssh/id_ed25519"}'
17:00:36 INF vault·create Created vault: "infra" ("Local Encryption")

swamp vault put <vault_name> <key> [value]

Store a secret in a vault.

Option Description
-f Skip confirmation when overwriting
--force Same as -f
$ swamp vault put my-secrets api-key sk-test-12345
17:00:04 INF vault·put Stored secret "api-key" in vault "my-secrets"

swamp vault list-keys <vault_name>

List all secret key names in a vault. Values are not shown.

$ swamp vault list-keys my-secrets
17:00:29 INF vault·list-keys Vault "my-secrets" ("local_encryption"): 2 key(s)
17:00:29 INF vault·list-keys   - "api-key"
17:00:29 INF vault·list-keys   - "db-password"

swamp vault search [query]

Search for vaults in the repository. Opens an interactive picker when run in a terminal, or returns JSON with --json.

$ swamp vault search --json
{
  "query": "",
  "results": [
    {
      "id": "3870058a-0008-48d3-84c1-031405ce9ebf",
      "name": "infra",
      "type": "local_encryption",
      "createdAt": "2026-04-07T17:00:36.582Z"
    },
    {
      "id": "0dd4c691-0b7b-4fae-9741-31e96a4efc69",
      "name": "my-secrets",
      "type": "local_encryption",
      "createdAt": "2026-04-07T16:59:59.039Z"
    }
  ]
}

swamp vault get <vault_name_or_id>

Show details of a vault configuration.

Option Description
--type Vault type (narrows search)
$ swamp vault get my-secrets
{
  "id": "0dd4c691-0b7b-4fae-9741-31e96a4efc69",
  "name": "my-secrets",
  "type": "local_encryption",
  "config": {
    "auto_generate": true,
    "base_dir": "/home/user/my-repo"
  },
  "createdAt": "2026-04-07T16:59:59.039Z",
  "storagePath": ".swamp/vault/local_encryption/0dd4c691-0b7b-4fae-9741-31e96a4efc69.yaml"
}

swamp vault describe <vault_name_or_id>

Show detailed vault information including configuration.

Option Description
--type Vault type (narrows search)

swamp vault edit [vault_name_or_id]

Open a vault configuration file in an editor. Opens an interactive picker when called without arguments.

Option Description
--type Vault type (narrows search)

swamp vault type search [query]

Search for available vault types (built-in and from extensions).

$ swamp vault type search --json
{
  "query": "",
  "results": [
    {
      "type": "local_encryption",
      "name": "Local Encryption",
      "description": "Store encrypted secrets in local files using AES-GCM encryption. Uses SSH private key or auto-generated key for encryption."
    }
  ]
}

Vault Provider Interface

All vault implementations — built-in and custom — implement this interface:

Method Signature Description
get (secretKey: string) => Promise<string> Retrieve a secret value by key
put (secretKey: string, secretValue: string) => Promise<void> Store a secret value
list () => Promise<string[]> List all secret key names (no values)
getName () => string Return the provider name/type

Complete Example

Create a vault, store secrets, and reference them in a model definition:

swamp vault create local_encryption prod-secrets
swamp vault put prod-secrets api-key sk-live-abc123
swamp vault put prod-secrets db-password s3cret-p4ss

Reference the secrets in a model definition:

type: command/shell
typeVersion: 2026.02.09.1
id: 53274ce2-c390-412b-93ac-48b205a13f4e
name: api-caller
version: 1
globalArguments:
  api_key: "${{ vault.get('prod-secrets', 'api-key') }}"
methods:
  execute:
    arguments:
      run: "curl -H \"Authorization: Bearer ${{ vault.get('prod-secrets', 'api-key') }}\" https://api.example.com"
      env:
        DB_PASSWORD: "${{ vault.get('prod-secrets', 'db-password') }}"

At execution time, vault.get() expressions resolve to the stored secret values. Shell commands receive secrets as environment variables — the raw values never appear in the command string.