deploy: 9-account/2-host packaging + static release build (Redmine 494)

Everything needed for one ~10-minute Charles window on ultix-streaming
and ultix-offstage, with zero root/systemd on the target accounts:

- deploy/accounts.tsv: the fleet authority (account, host, vertical,
  Redmine project, quota group, ports). Port scheme events=4100+index,
  serve=8090+index with a global 0-8 index so per-account daemons on
  one host never collide; loop state is per-account under ~/.mopac.
- deploy/install-account.sh <account>: idempotent installer run AS the
  target user; renders harness.toml from deploy/harness.toml.in, writes
  a 0600 env-secrets template, refuses to overwrite existing config or
  secrets (re-run = the upgrade path), generates mopac-start/stop.
- deploy/runbook.md: exact Charles sequence (build, stage, install,
  secrets bootstrap, verify, start via nohup or cron @reboot, rollback)
  with the account-port table, per-host time estimates and assumptions.
- Makefile: release target (digest-pinned docker builder, CGO off,
  linux/amd64 static, stripped) plus check/deploy-test entrypoints.
- deploy/tests.sh: 13 packaging tests (TSV scheme, template substitution
  for all 9 accounts, idempotence, refuse-to-overwrite, binary lookup,
  rendered config loads via a dry-run); README deploy section.

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
This commit is contained in:
2026-08-29 05:50:41 -05:00
parent fc518c475e
commit b7799ea0de
7 changed files with 889 additions and 1 deletions
+46 -1
View File
@@ -17,7 +17,8 @@ logged reason and is reconsidered next scan, never hard-failed; per-class
token+credit accounting lands in the loop JSONL (`harness quota status`).
`harness serve` (OWUI front door, LAN 8090), `harness loop` (fake-Redmine
e2e test-asserted), the MVP demo path and `harness events` (port 4100)
all live.
all live. Multi-account deploy packaging (Redmine 494): `make release` +
`deploy/` — 9 accounts / 2 hosts, per-account ports + state, no root.
## Quickstart
@@ -46,6 +47,11 @@ docker run --rm -v "$PWD:/h" -w /h \
(that digest = `golang:1.26-bookworm`; alpine lacks bash, which the exec
tool's tests need.)
`make release` builds the deployable static binary
(`bin/harness-linux-amd64`, CGO off) through the same pinned builder;
`make deploy-test` tests the multi-account packaging — see
"Deploy: 9 accounts / 2 hosts" below.
Expected output (tail):
```text
@@ -565,10 +571,49 @@ docker run ... --memory 4g --cpus 2 --pids-limit 512 \
or a systemd slice for non-container deploys (`CPUQuota=200%`,
`MemoryMax=4G`, `IOWeight`). The gate catches what the limits don't.
## Deploy: 9 accounts / 2 hosts (Redmine 494)
The full multi-account packaging lives in [`deploy/`](deploy/) — one
harness instance per Linux account (5 on ultix-streaming, 4 on
ultix-offstage), each entirely under that account's `~/.mopac/` (binary,
config, 0600 env secrets, state, reports). No root, no systemd, no host
packages: `sudo` is only used to switch identity when installing into the
other accounts.
```sh
make release # static linux/amd64 binary: bin/harness-linux-amd64
# (docker builder, CGO_ENABLED=0 — runs anywhere)
make deploy-test # packaging tests: port scheme, template substitution,
# installer idempotence in a fake HOME
```
- [`deploy/accounts.tsv`](deploy/accounts.tsv) — the fleet authority:
account, host, vertical, Redmine project, quota group, and the port
scheme `events = 4100 + index` / `serve = 8090 + index` (index 0-8, so
the daemons of any two accounts on one host never collide; loop state
is per-account under `~/.mopac/state/`).
- [`deploy/install-account.sh <account>`](deploy/install-account.sh) —
idempotent installer, run AS the target user: creates
`~/.mopac/{bin,state,reports,work}`, installs the static binary,
renders `harness.toml` from [`deploy/harness.toml.in`](deploy/harness.toml.in)
+ the TSV row, writes the 0600 `~/.mopac/env` secrets template and
`mopac-start`/`mopac-stop` helpers. Existing `harness.toml`/`env` are
NEVER overwritten (re-running = the upgrade path).
- [`deploy/runbook.md`](deploy/runbook.md) — the exact Charles sequence:
build once, stage per host, install per account, secrets bootstrap,
verify (`once --dry-run --demo`, healthz, first `loop --once --dry-run`),
start (nohup helpers + optional cron `@reboot`), rollback (stop +
`rm -rf ~/.mopac`), per-host time estimates.
Generated configs keep `[quota]`/`[resources]` commented with values
pre-filled per account — flipping them on is a per-account one-liner
once the shared redis container (above) is up.
## Docs and links
- [DESIGN.md](DESIGN.md) — design spec (hard rules, build order, org model)
- [REPORT.md](REPORT.md) — current build status
- [docs/SPEC-20260829-charles-brief.md](docs/SPEC-20260829-charles-brief.md) — Charles's end-to-end design brief (spec of record)
- [docs/PORTING-NOTES-crush.md](docs/PORTING-NOTES-crush.md) — sessions/MCP/provider study of the crush agent
- [docs/PORTING-NOTES-maki.md](docs/PORTING-NOTES-maki.md) — permission parsing and token-reduction study of maki
- [docs/PORTING-NOTES-secrets.md](docs/PORTING-NOTES-secrets.md) — Bitwarden secrets study; feeds the bitwarden-go tool