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.encThe 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-1234517: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-secrets17: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:
globalArgumentsvalues in model definitionsmethods.*.argumentsvalues in model definitions- Step task
inputsin workflows forEach.inexpressions in workflows
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
- During expression evaluation, each
vault.get()call is replaced with an internal sentinel token. - Before shell execution, each sentinel is replaced with a shell variable
reference (
${__SWAMP_VAULT_N}), whereNis a sequential index. - The raw secret values are passed as environment variables to the process.
- 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-12345A 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:
- Field-level
vaultNamefrom schema metadata - Spec-level
vaultNamefrom the resource output specification - 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-secrets17: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-1234517: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-secrets17: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-p4ssReference 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.