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>
Two changes based on further review:
- Explicitly considers and rejects letting keep's server execute the
redeploy directly on announce, even though co-location removes the
credential/network objection that sank the CI-SSHes-in alternative.
The reason it's still wrong: a fully compromised keep server today
can't do anything worse than leak ciphertext, since it never holds a
decryption key. Giving it exec capability breaks that property — a
routine web app bug becomes "triggers arbitrary redeploys," not
"leaks encrypted blobs." Also notes the concrete cost: doing this
from inside a container needs the Docker socket mounted in, which is
broadly equivalent to root on the host.
- Replaces the "vault key = deploy directory name" assumption with an
explicit .keep-vault marker file per project directory. Repo name,
deploy directory name, and vault key are three independent strings
in practice (confirmed by a real example already in this fleet) —
no naming convention should assume any two of them match. Bootstrap
is now: choose a vault key, keep push it, drop a one-line
.keep-vault file. CI's announce step uses the same deliberately
hardcoded key, never derived from the repo name automatically.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The original draft speced a hypothetical per-project agent.sh on a
per-project cron. Reality turned out different: there's already a
production bulk deploy script on the host (fixed project array,
docker compose up -d per project, no explicit pull — freshness comes
entirely from pull_policy: always) — so this patches that existing
loop in place instead of introducing a parallel per-project script
that would duplicate its array/failure-tracking logic.
Also folds in a real prerequisite gap found while reconciling the
proposal with the actual script: pull_policy: always isn't universal
across the fleet yet, independent of keep entirely — the bulk script
can't redeploy anything missing that line regardless of keep adoption.
One instance of this was already fixed this session (a sibling
project's docker-compose.yml missed the line when copying another
project's compose pattern).
Reframes §5's announce/watch benefit accordingly: since the real
script is one all-or-nothing sweep, not N independent watchers, the
natural fit is waking the whole sweep early (watch-any), not
per-project selective redeploy — still deliberately left unbuilt
until there's a real signal it's worth it over the already-scheduled
sweep.
Project names anonymized per policy — generic proj1..proj12
placeholders instead of the real fleet.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Design doc for closing the CI->deploy gap without reintroducing the
credential-concentration problem a naive "CI SSHes into the VPS"
approach would bring back. keep stays a pure relay — never executes
anything remotely. CI announces a new tag (a distinct verb from push/
pull, since a tag isn't a secret and doesn't belong in the encrypted
vault payload); each VPS polls and redeploys itself locally, using an
identity and grant it already holds.
Explicitly specs the not-yet-bootstrapped fallback: the agent script's
`keep pull` is allowed to fail for any reason, and on failure just runs
a plain `docker compose pull && up -d` against whatever's already on
disk — the same thing that would have happened before keep existed.
Ships the dumb-periodic-timer version of the agent first; the watch/
announce fast path is speced but deliberately left unbuilt until
there's a real signal that polling latency is worth the complexity.
Not implemented — design stage.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>