Server: Express + better-sqlite3 (WAL), multi-recipient key-wrapping per IMPLEMENTATION.md's design — vaults/recipients/vault_grants/ access_log. Two auth paths: ADMIN_PASSWORD header for recipient management and revoke (pure metadata operations), signed-request auth (Ed25519 signature over method+path+timestamp+body-hash) for push/pull/grant, mirroring the spirit of this project family's other signed-handshake patterns without naming them. CLI: identity init/show/set-id, push/pull/grant/log, admin recipient add/list/remove and revoke. Grant is a client-side crypto operation (the granter unwraps the vault's current key locally and reseals it for the new recipient) rather than a server-side operation, since the server never holds an unwrapped key to grant with. Verified end-to-end with two independent local identities against a live server and separately against the built Docker image: register, push, pull (granted and ungranted), grant without re-pushing, admin revoke, a subsequent rotation confirming the revoked recipient stays excluded, and rejection of missing/malformed signed-request auth. Two real bugs caught during verification, not just written up: - libsodium-wrappers' published ESM build does a relative import only resolvable under bundler-style resolution — broken under plain Node ESM. Fixed via createRequire to force the CJS build. - Express's req.path inside a sub-router is relative to the mount point, which would have silently mismatched a client signing the full request path. Fixed by verifying against req.originalUrl. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
88 lines
3.5 KiB
Markdown
88 lines
3.5 KiB
Markdown
# Incorporating keep into an existing deploy setup
|
|
|
|
Concrete, by deploy *pattern* rather than by naming specific projects —
|
|
the shape of the fit depends on how a project currently gets its config
|
|
onto a target machine, not on what the project is.
|
|
|
|
## The general shape
|
|
|
|
Wherever a deploy script or a manually-maintained `.env` exists today, the
|
|
change is the same: replace "assume `.env` is already sitting there,
|
|
hand-copied at some point" with a `keep pull` at the top of the script.
|
|
|
|
```bash
|
|
# before — a deploy script assumes .env already exists on this machine
|
|
set -euo pipefail
|
|
source .env # or docker-compose reads it directly
|
|
|
|
# after
|
|
set -euo pipefail
|
|
keep pull <project>/<env> --out .env
|
|
```
|
|
|
|
The one-time setup cost per machine: `keep identity init` once, send the
|
|
printed public key to whoever runs `keep recipient add`, then
|
|
`keep identity set-id <id>` with what comes back. After that, every future
|
|
deploy from that machine just works — no more "wait, what's the DB
|
|
password again" when setting up a new box.
|
|
|
|
## Pattern: docker-compose reading a local `.env`
|
|
|
|
A common shape — `docker compose up -d` reads `environment:` values from
|
|
a `.env` file sitting next to `docker-compose.yml` on the deploy machine,
|
|
hand-maintained and containing real secrets.
|
|
|
|
```bash
|
|
keep pull myapp/production --out .env
|
|
docker compose up -d
|
|
```
|
|
|
|
No code changes needed in the target project — `keep` only changes *how
|
|
the `.env` file gets there*, not what reads it. This is the easiest
|
|
category to retrofit.
|
|
|
|
## Pattern: cross-compile + scp + SSH-restart deploy scripts
|
|
|
|
A shape where a deploy script builds a binary locally, ships it to a VPS,
|
|
and restarts a service over SSH — reading a local `.env` next to the
|
|
script for things like shared secrets that shouldn't be hardcoded or
|
|
re-typed on every invocation.
|
|
|
|
```bash
|
|
keep pull myapp/production --out .env
|
|
set -a && source .env && set +a
|
|
# ...rest of the deploy script unchanged
|
|
```
|
|
|
|
Same retrofit as the docker-compose case, just applied before whatever
|
|
build/ship step already exists.
|
|
|
|
## Pattern: a plaintext config file served to a browser
|
|
|
|
Not every `config.js`/similar file that *looks* like environment config
|
|
actually holds secrets — some of it is public-by-design (an API base URL,
|
|
a feature flag) that's explicitly meant to be visible to anyone loading
|
|
the page. `keep` solves a confidentiality problem; a file with nothing
|
|
confidential in it doesn't have one. Worth checking this distinction
|
|
before wiring `keep` into anything — retrofitting a non-secret config
|
|
file would be solving a problem that file doesn't have.
|
|
|
|
## A vault server's own configuration
|
|
|
|
Whatever runs `keep` itself needs its own `ADMIN_PASSWORD` to be a real,
|
|
manually-set secret on its own host — it can't bootstrap its own trust
|
|
root by pulling itself from itself. This is the same "root of trust
|
|
starts somewhere unmanaged" fact every secrets system has (Vault's own
|
|
unseal keys aren't stored in Vault either). Not a gap, just worth being
|
|
explicit about: `keep`'s own deploy stays on a plain `.env`/
|
|
`docker-compose.yml` environment variables.
|
|
|
|
## New projects going forward
|
|
|
|
The actual best use case isn't retrofitting existing deploys — it's never
|
|
having the "hand-reconstruct a `.env` from memory" moment for a *new*
|
|
deploy target in the first place. For whatever gets built next: register
|
|
its production host as a `keep` recipient as part of first setup, write
|
|
the deploy script to `keep pull` from day one, and the annoyance this was
|
|
built to fix never has a chance to happen.
|