Implement announce/watch: a deploy signal that reverses CI's reach
All checks were successful
Docker / build-and-push (push) Successful in 1m58s

CI announces a new image tag to keep (any grant is enough, read-only
included); each deploy target polls for it locally instead of CI
holding standing SSH access to production. keep stays a pure relay —
the tag lives outside the encrypted vault payload in its own table,
and keep never executes anything itself.

Adds vault_announcements, POST/GET /vaults/:key/announce, and the
`keep announce`/`keep watch` CLI commands, per
PROPOSAL-announce-and-agent.md (now marked implemented). Verified live:
a read-only identity announcing successfully and being logged, watch
picking up the tag on its first poll, and watch exiting nonzero for an
unknown/ungranted vault.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Fredrik Johansson
2026-07-12 21:12:55 +02:00
parent 674e408f09
commit 58d5d787dd
7 changed files with 225 additions and 11 deletions

View File

@@ -141,6 +141,29 @@ Pushing to an existing vault requires a *write* grant — a read-only
recipient can pull but can't push or grant others access. A brand-new
vault's first push is always read-write for the pusher.
## Announcing deploys
`keep` can also relay a small "there's a new image" signal alongside a
vault, so CI never needs standing SSH access to a deploy target — CI
announces a tag, and whatever already redeploys that box locally polls
for it instead of being reached into from outside:
```bash
# CI, after a successful build/push — any grant is enough, read-only included:
keep announce myapp/production --tag <short-sha>
# The deploy target polls for a change and prints it:
keep watch myapp/production --interval 60
```
An announced tag isn't a secret — it lives outside the encrypted vault
payload, in its own small table, and is visible to the server (same as
vault names and access timestamps already are). `keep` never pulls or
runs the announced image itself; it only relays the string. See
[IMPLEMENTATION.md](./IMPLEMENTATION.md) for how this fits into a bulk
deploy script with a no-`keep` fallback for projects not yet bootstrapped
onto it.
## Docker
```bash
@@ -177,12 +200,16 @@ CLI-side: `KEEP_SERVER_URL` (default `http://localhost:3050`),
Implemented: identity, push/pull/grant/revoke/log, read/write grant
scoping, one-step rollback with an explicit purge mode for
compromise-driven rotations, admin recipient management, Docker deploy.
Verified end-to-end — two and three independent identities, read-only
grants correctly blocked from push/grant, grants preserved across
rotation, previous-version pull working and correctly wiped by
`--purge` for every recipient, malformed-auth rejection — against both
a local server and the built Docker image. See
compromise-driven rotations, admin recipient management, an admin
cross-vault `overview`, deploy-signal `announce`/`watch`, a
`scripts/deploy.sh` wrapper, and Docker deploy. Verified end-to-end —
two and three independent identities, read-only grants correctly
blocked from push/grant, grants preserved across rotation,
previous-version pull working and correctly wiped by `--purge` for
every recipient, a read-only identity successfully announcing while
still being logged, `watch` picking up an announced tag and exiting
nonzero for an unknown/ungranted vault, malformed-auth rejection —
against both a local server and the built Docker image. See
[IMPLEMENTATION.md](./IMPLEMENTATION.md) for the full design and its
resolved decisions (per-secret-key granularity deliberately not built —
use multiple vaults instead).