Skip to main content
← Back to list
01Issue
FeatureShippedSwamp CLIPublic
Assigneesstack72

Relationships

#3259 repo init: create a default local_encryption vault, and stop persisting absolute base_dir in vault configs

Opened by stack72 · 10/11/2026· Shipped 10/11/2026

Problem

Every new swamp repo gets a working datastore without any setup, but no vault. A vault is opt-in: you only have one after running swamp vault create. As a result, the first model with a sensitive output fails on a fresh repo with "no vault is configured. Create a vault using: swamp vault create ". Storing a first secret also means picking a vault type before doing anything else. Secret storage should work out of the box, just as data storage does.

Proposed solution

From now on, swamp repo init creates a local_encryption vault named default and makes it the repo's default vault.

  • What init writes: vaults/local_encryption/<uuid>.yaml with name: default, type: local_encryption, config: { auto_generate: true }. base_dir is left out on purpose, because VaultService.fromRepository already fills it in from the repo directory when vaults load. That keeps the committed file portable across clones and machines.
  • .swamp.yaml: the same marker write that init already does sets defaultVault: default. Sensitive outputs and serve vault operations then resolve to it through the existing resolution order.
  • No key material at init: with auto_generate, the key is created on the first secret write, under .swamp/secrets/local_encryption/default/.key. secrets is in ALWAYS_LOCAL_SUBDIRS and .swamp/ is gitignored, so neither the key nor the secrets reach git or a remote datastore.
  • Opt-out: swamp repo init --no-vault skips creating the vault.
  • --force re-init: if a vault named default already exists (any type), it is left untouched, and a defaultVault that is already set is never changed.
  • --server init: the vault is still created. It is harmless and costs only a YAML file.
  • Output: RepoInitData gains vault: { name, type } | null for --json, and log mode prints a line saying the vault was created.
  • Remote-datastore warning: swamp datastore setup warns when the repo's defaultVault is a local_encryption vault and the datastore being configured is remote. Secrets in that vault are per machine, so teammates sharing the datastore cannot read values it holds.
  • Scope: new repos only. swamp repo upgrade and existing repos are unchanged, so no existing repo's behaviour changes. For example, a sensitive output in an existing repo with no vault still fails loudly.

Tests

  • Unit tests for the init paths: default creation, --no-vault, --force with an existing default vault, and an existing defaultVault.
  • Update in-repo fixtures that call repoInit and assume a fresh repo has no vaults.
  • List the swamp-uat expectations that need updating (empty vault list after init, and the "no vault is configured" error on a fresh repo).

Docs

design/surfaces/repo.md, design/primitives/vaults.md (default vault / resolution order), the swamp skill's vault guide and troubleshooting, swamp-getting-started, and the share joiner instructions. The joiner instructions should say that the default vault is per machine and starts empty on a fresh clone.

Also in scope: stop persisting an absolute base_dir for local_encryption vaults

Today swamp vault create local_encryption <name> writes base_dir: <absolute repo path on this host> into the committed vault YAML (resolveBuiltInProviderConfig in src/libswamp/vaults/create.ts, unchanged since 2026-01-31). On a teammate's clone that path points at someone else's home directory. Under managedConfig the config is synced to every instance, so every instance gets the original host's path.

The same absolute path is written by:

  • vault migrate to local_encryption (resolveBuiltInProviderConfig in src/libswamp/vaults/migrate.ts)
  • the serve-side key-source defaulting withServerDefaultKeySource / serverDefaultKeySource (src/domain/vaults/local_encryption_key_source.ts), which vault create, vault migrate and vault edit repair use over --server

Loading already handles an unset base_dir: VaultService.fromRepository and src/cli/datastore_expression_resolver.ts fill it in from the repo directory. Fix:

  • Stop writing it. None of these paths writes base_dir any more. An unset base_dir means "this repo's directory".
  • One shared runtime fill-in. A single domain helper resolves the runtime config (adds the repo dir when base_dir is unset). VaultService, the datastore expression resolver and vault migrate all use it. Today vault migrate builds the target provider directly, and would fall back to Deno.cwd() instead of the repo dir under --repo-dir.
  • Serve keeps its swamp-club#2690 guarantee. A remote caller still cannot choose the key source. A base_dir exactly equal to the server's repo dir is still accepted, and is stored as unset.
  • Explicit paths are kept. A base_dir a local user deliberately sets to a different path via --config is still persisted as given.
  • Existing vault files are never rewritten. Neither swamp repo upgrade nor any other command changes a stored base_dir. Existing vault YAML keeps whatever it has today, and only newly written configs drop the absolute path. Users can remove it themselves with swamp vault edit.

Alternatives considered

  • Implicit vault, like the implicit filesystem datastore. VaultService.ensureDefaultVaults(), now a no-op, would register a virtual default vault whenever none is configured. Rejected for three reasons:

    • It changes behaviour in every existing repo: sensitive outputs that fail loudly today would start succeeding silently.
    • vault list/edit/delete/migrate, the serve vault APIs and managedConfig would all need special cases.
    • The vault could never be deleted.

    A vault is user-managed configuration referenced by name in expressions, so it should be a real config file.

  • Leave defaultVault unset and rely on the "first user vault" fallback. Rejected because adding a second vault (e.g. @swamp/aws-sm) would make where sensitive outputs go depend on file ordering. An explicit defaultVault: default is predictable, and users change it when they want another vault to be the default.

02Bog Flow
✓OPEN✓TRIAGED✓IN PROGRESS✓SHIPPED+ 1 MOREASSIGNED+ 8 MOREREVIEW+ 10 MOREPR_MERGED+ 2 MORESESSION_SUMMARIZED

Shipped

10/11/2026, 10:25:54 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
stack72 assigned stack7210/11/2026, 8:58:43 PM

Sign in to post a ripple.