Export the reviewed allowlisted snapshot from private source commit 07c1655921f21ee5e4fc4d85639d199e8867b17d. This records the Docker Compose activation, schema-compatible rollback, and stateful migration resource findings from Observatory Preview 19 dogfooding. AI-Assisted: OpenAI Codex Signed-off-by: Cole Speelman <crspeelman@gmail.com>
95 lines
4.8 KiB
Markdown
95 lines
4.8 KiB
Markdown
# Architecture
|
|
|
|
Tend has three deliberately separate boundaries.
|
|
|
|
## Package boundary
|
|
|
|
`tend package` accepts only a clean checkout whose `HEAD` exactly matches the
|
|
configured pushed branch. It rejects Go module replacements and unversioned
|
|
dependencies, builds the configured main package twice with `GOWORK=off`, and
|
|
requires byte-identical output. The resulting archive contains only:
|
|
|
|
- the service binary;
|
|
- `RELEASE.json`;
|
|
- `BUILDINFO.json`;
|
|
- `SBOM.spdx.json`; and
|
|
- `SHA256SUMS`.
|
|
|
|
The external archive digest is the release identity used by deployment.
|
|
|
|
## Transport boundary
|
|
|
|
`tend push` sends one bounded protocol frame to a dedicated account through a
|
|
pinned OpenSSH host key. It uses an argument vector, disables config files,
|
|
forwarding, PTYs, local commands, and proxy commands, and requests exactly
|
|
`tend-receive-v1`. The account's forced command invokes only the root-owned
|
|
receiver. The receiver's root-owned policy maps an allowlisted service name to
|
|
one exact configuration path and size ceiling.
|
|
|
|
The target receives no source, repository credential, Go cache, dependency, or
|
|
arbitrary command. The artifact digest must equal both the produced digest and
|
|
the separately supplied approved digest before it is staged.
|
|
|
|
## Target-host boundary
|
|
|
|
Schema-2 configurations live below `/etc/tend/services/`. Secrets live only in
|
|
separate `/etc/tend/environment/*.env` files that are root-owned, non-symlink,
|
|
and mode `0600`. Configurations contain the path, never the values. Candidate
|
|
and installed units read the same file; Tend overrides only the candidate's
|
|
loopback listen address.
|
|
|
|
Dry-run validation extracts into a temporary directory and does not touch the
|
|
release tree. Activation acquires the shared lock, installs a content-addressed
|
|
release, verifies all embedded checksums and manifest fields, and starts the
|
|
candidate without evaluating configuration as shell code.
|
|
|
|
## Activation boundary
|
|
|
|
Blue/green mode points the inactive slot at the new release, restarts and
|
|
probes it, atomically replaces one imported Caddy handler, validates the full
|
|
Caddy configuration, reloads Caddy, and records the prior active slot.
|
|
|
|
Singleton mode starts the target release in a hardened transient systemd unit on a
|
|
separate loopback address and probes it. The unit is named from the service and
|
|
a fresh bounded operation ID; a separate strict candidate-lease file binds that
|
|
unit to the candidate release, address, and start time without changing the
|
|
schema-1 deployment-state contract used by retained recovery binaries. Tend
|
|
then validates and atomically
|
|
routes the imported Caddy handler to that candidate. While the candidate serves
|
|
the canonical origin, Tend changes the current release pointer, restarts and
|
|
probes the installed fixed-address unit, validates Caddy again, and routes back
|
|
to it. The candidate remains healthy through the activation window and stops
|
|
only after the handoff succeeds.
|
|
|
|
After the local post-activation probes, Tend also checks configured HTTPS public
|
|
origins throughout a bounded activation window. Blue/green mode also probes the
|
|
previous slot, and singleton mode probes the handoff candidate, for health and
|
|
readiness throughout that window. Any failure before success is recorded
|
|
restores the previously observed Caddy bytes and/or release pointers. State
|
|
records desired, candidate, active, previous, and last-attempt
|
|
release identities, including failed attempts without claiming they became
|
|
active. Rollback is a separate explicit command
|
|
over the recorded state. It rechecks local health/readiness and public reachability,
|
|
but deliberately does not apply a future release's content marker to an older
|
|
release whose routes may differ. Pruning preserves both active and previous releases.
|
|
|
|
`tend reconcile` is a read-only observation boundary. It compares persisted
|
|
state, systemd activity, current/previous release pointers, and exact handler
|
|
file bytes. It does not claim to inspect Caddy's currently loaded in-memory
|
|
configuration, and it never clears or stops a candidate. The report makes
|
|
retained, inactive, ambiguous, and settled states legible before a future
|
|
explicit recovery operation is approved.
|
|
|
|
## Evidence boundary
|
|
|
|
Every attempted activation and explicit rollback emits bounded, versioned JSONL
|
|
events with an operation ID, service, approved artifact digest, source commit,
|
|
release version, phase, slot, elapsed duration, and outcome. Values are
|
|
validated rather than copied from command output. The log contains no
|
|
environment values, arbitrary process output, HTTP bodies, or secret paths.
|
|
Release-identity, event-file, and downstream observability failures are
|
|
deliberately best effort and cannot control Tend's deployment or rollback
|
|
result. A fresh operation identity is operationally required for a singleton
|
|
candidate unit; entropy failure therefore stops either forward activation or
|
|
rollback before a candidate process or traffic change.
|