Sync the self-hosting stack

This commit is contained in:
2026-08-27 14:42:54 +02:00
parent fa19933a32
commit aa1dfb5b02
3 changed files with 114 additions and 11 deletions
+23 -10
View File
@@ -156,8 +156,7 @@ round has run, so replaying it re-generates intent and never re-runs an operatio
each loop and lets it write out what it had streamed with a note saying why the transcript ends
there; the next process finishes that message, closes any tool call whose outcome is unknown saying
in as many words that it is unknown, and resumes the run — instructed to read the current state
before repeating anything that writes. This is why the api asks for a stop grace period, and part of
why it must run as a single replica.
before repeating anything that writes. This is why the api asks for a stop grace period.
**The agent dials out.** Nothing inbound is opened on a managed machine. It holds a websocket to
your instance, signs each session with a key whose private half never leaves it, and reports its
@@ -398,14 +397,28 @@ These are deliberate and documented rather than surprises waiting to be found.
password changed to match, so plan that one.
- **SSH host keys are not verified.** Every SSH connection trusts whatever key answers. This is the
one gap in the execution path with no compensating control.
- **The api must run as a single replica.** In-flight runs, pending approvals, presence and the
websocket of every enrolled agent live in one process's memory. The Redis in this stack does not
lift that limit: it fans outgoing events out to other replicas, but an incoming one is only ever
handled by the replica holding that connection. So a cancel or an approval answered on the wrong
replica is silently dropped, and an agent tool call can land on a replica that does not hold the
target machine. Worse, a starting replica's recovery sweep claims every run it does not own — so a
second instance re-executes, against your real infrastructure, operations the first is still
running.
- **This compose file runs one api, and that is the shape it is written for.** It gives you one of
everything and generates the secrets onto a shared volume, which is right here and is exactly what
must not be done per replica anywhere else — each copy would mint its own vault key, and a
credential one process sealed would read as corrupt to another with nothing logged at boot.
The images themselves no longer require a single replica. The web container's upstream and DNS
resolver are configurable, there are separate liveness and readiness endpoints, both containers run
unprivileged, and the schema is a separate entrypoint meant to run once rather than something every
container does at boot. Everything that used to do real damage across replicas is fixed: a starting
replica's recovery sweep once claimed every run it did not own, so a second instance re-executed —
against your real infrastructure — operations the first was still running. A run now holds its
conversation through a unique index in the database, a sweep claims only a run whose heartbeat has
gone cold, and a run that cannot write its heartbeat stops itself before anyone else may take over.
Agent tool calls, revocations, Stop, approvals and interactive terminals reach whichever process
holds the connection instead of failing where they land. A replica shutting down no longer marks
your whole fleet offline, and starting one no longer writes an ending into the record of a root
shell that is still open.
**Nobody has run an estate on more than one replica yet**, so if you are the first, treat it as
something to watch rather than something to assume. Two API processes have been run together
against one database and one Redis, and the manifests are published, but that is not the same
thing as your machines depending on it.
- **There is no rate limiting on sign-in.** No throttler, no account lockout, and authentication
events are not audited. Credential stuffing is bounded only by the reverse proxy you put in front,
which this repository does not ship. If your proxy can rate-limit one route, make it that one.