feat: consolidate secret stores — single granular secrets/*.enc, deprecate .env.vault.enc #777

Closed
opened 2026-04-15 09:36:50 +00:00 by dev-bot · 1 comment
Collaborator

Problem

Two parallel secret stores:

  1. secrets/<NAME>.enc — per-key, age-encrypted. Populated by disinto secrets add. No runtime consumer today. Only disinto secrets show ever decrypts these.
  2. .env.vault.enc — monolithic, sops/dotenv-encrypted. The only store actually loaded into containers (via docker/edge/dispatcher.shsops -d --output-type dotenv).

Two mental models, redundant subcommands (edit-vault, show-vault, migrate-vault), and today's disinto secrets add silently deposits secrets into a dead-letter directory. Operator runs the command, edge container still logs CADDY_SSH_KEY not set, skipping (docker/edge/entrypoint-edge.sh:207).

Proposed solution

Consolidate on secrets/<NAME>.enc as THE store. One file per secret, granular, small surface.

1. Wire container dispatchers to load secrets/*.enc into env

  • docker/edge/dispatcher.sh (and agent / ops dispatchers) decrypt declared secrets at startup and export them.
  • Granular per-secret — not a bulk dump.

2. Containers declare required secrets

  • secrets.required = ["CADDY_SSH_KEY", "CADDY_SSH_HOST", ...] in the container's TOML, or equivalent in compose.
  • Missing required secret → hard fail with clear message. Replaces today's silent-skip branch at entrypoint-edge.sh:207.

3. Deprecate the monolithic vault

  • Remove .env.vault, .env.vault.enc, and subcommands edit-vault / show-vault / migrate-vault from bin/disinto.
  • Remove sops round-trip from docker/edge/dispatcher.sh (lines 32-40 currently).

4. One-shot migration for existing operators

  • disinto secrets migrate-from-vault splits an existing .env.vault.enc into secrets/<KEY>.enc files, verifies each, then removes the old vault on success.
  • Idempotent: safe to run multiple times.

Context

  • Parent: sprint PR disinto-admin/disinto-ops#10.
  • Depends on: #776 (secrets add must accept piped stdin before we can deprecate edit-vault) — now closed.
  • Rationale (operator quote): "containers should have option to load single secrets, granular. no 2 mental models, only 1 thing that works well and has small surface."

Acceptance criteria

  • Edge container declares secrets.required = ["CADDY_SSH_KEY", "CADDY_SSH_HOST", "CADDY_SSH_USER", "CADDY_ACCESS_LOG"]; dispatcher exports them; collect-engagement.sh runs without additional env wiring
  • Container refuses to start when a required secret is missing (fail loudly, not skip silently)
  • .env.vault* files and all vault-specific subcommands removed from bin/disinto and all formulas / docs
  • migrate-from-vault converts an existing monolithic vault correctly (verified by round-trip test)
  • disinto secrets help text shows one store, four verbs: add, show, remove, list

Affected files

  • bin/disinto — remove edit-vault, show-vault, migrate-vault subcommands; add migrate-from-vault
  • docker/edge/dispatcher.sh — replace sops round-trip with per-secret age decryption (lines 32-40)
  • docker/edge/entrypoint-edge.sh — replace silent-skip at line 207 with hard fail on missing required secrets
  • lib/vault.sh — update or remove vault-env.sh wiring now that .env.vault.enc is deprecated
## Problem Two parallel secret stores: 1. `secrets/<NAME>.enc` — per-key, age-encrypted. Populated by `disinto secrets add`. **No runtime consumer today.** Only `disinto secrets show` ever decrypts these. 2. `.env.vault.enc` — monolithic, sops/dotenv-encrypted. The only store actually loaded into containers (via `docker/edge/dispatcher.sh` → `sops -d --output-type dotenv`). Two mental models, redundant subcommands (`edit-vault`, `show-vault`, `migrate-vault`), and today's `disinto secrets add` silently deposits secrets into a dead-letter directory. Operator runs the command, edge container still logs `CADDY_SSH_KEY not set, skipping` (docker/edge/entrypoint-edge.sh:207). ## Proposed solution Consolidate on `secrets/<NAME>.enc` as THE store. One file per secret, granular, small surface. **1. Wire container dispatchers to load `secrets/*.enc` into env** - `docker/edge/dispatcher.sh` (and agent / ops dispatchers) decrypt declared secrets at startup and export them. - Granular per-secret — not a bulk dump. **2. Containers declare required secrets** - `secrets.required = ["CADDY_SSH_KEY", "CADDY_SSH_HOST", ...]` in the container's TOML, or equivalent in compose. - Missing required secret → **hard fail** with clear message. Replaces today's silent-skip branch at `entrypoint-edge.sh:207`. **3. Deprecate the monolithic vault** - Remove `.env.vault`, `.env.vault.enc`, and subcommands `edit-vault` / `show-vault` / `migrate-vault` from `bin/disinto`. - Remove sops round-trip from `docker/edge/dispatcher.sh` (lines 32-40 currently). **4. One-shot migration for existing operators** - `disinto secrets migrate-from-vault` splits an existing `.env.vault.enc` into `secrets/<KEY>.enc` files, verifies each, then removes the old vault on success. - Idempotent: safe to run multiple times. ## Context - Parent: sprint PR `disinto-admin/disinto-ops#10`. - Depends on: #776 (`secrets add` must accept piped stdin before we can deprecate `edit-vault`) — now closed. - Rationale (operator quote): *"containers should have option to load single secrets, granular. no 2 mental models, only 1 thing that works well and has small surface."* ## Acceptance criteria - [ ] Edge container declares `secrets.required = ["CADDY_SSH_KEY", "CADDY_SSH_HOST", "CADDY_SSH_USER", "CADDY_ACCESS_LOG"]`; dispatcher exports them; `collect-engagement.sh` runs without additional env wiring - [ ] Container refuses to start when a required secret is missing (fail loudly, not skip silently) - [ ] `.env.vault*` files and all vault-specific subcommands removed from `bin/disinto` and all formulas / docs - [ ] `migrate-from-vault` converts an existing monolithic vault correctly (verified by round-trip test) - [ ] `disinto secrets` help text shows one store, four verbs: `add`, `show`, `remove`, `list` ## Affected files - `bin/disinto` — remove `edit-vault`, `show-vault`, `migrate-vault` subcommands; add `migrate-from-vault` - `docker/edge/dispatcher.sh` — replace sops round-trip with per-secret age decryption (lines 32-40) - `docker/edge/entrypoint-edge.sh` — replace silent-skip at line 207 with hard fail on missing required secrets - `lib/vault.sh` — update or remove vault-env.sh wiring now that `.env.vault.enc` is deprecated
dev-bot added the
backlog
priority
labels 2026-04-15 14:03:46 +00:00
dev-bot self-assigned this 2026-04-15 14:41:51 +00:00
dev-bot added
in-progress
and removed
backlog
labels 2026-04-15 14:41:51 +00:00
Author
Collaborator

Blocked — issue #777

Field Value
Exit reason no_push
Timestamp 2026-04-15T14:42:14Z
Diagnostic output
Claude did not push branch fix/issue-777
### Blocked — issue #777 | Field | Value | |---|---| | Exit reason | `no_push` | | Timestamp | `2026-04-15T14:42:14Z` | <details><summary>Diagnostic output</summary> ``` Claude did not push branch fix/issue-777 ``` </details>
dev-bot added
blocked
and removed
in-progress
labels 2026-04-15 14:42:14 +00:00
gardener-bot added
backlog
and removed
blocked
labels 2026-04-15 18:23:26 +00:00
dev-bot added
in-progress
and removed
backlog
labels 2026-04-15 18:27:02 +00:00
dev-bot removed their assignment 2026-04-15 18:46:13 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: disinto-admin/disinto#777
No description provided.