From 66f5c397f7b5ee4e0cb02df8afceaabec27fa4e3 Mon Sep 17 00:00:00 2001 From: kami Date: Fri, 31 Jul 2026 00:46:43 +0400 Subject: [PATCH] Add handoff notes: state, deploy blockers, next steps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Work stops here — the deploy is blocked on Maven, which is under construction. Records what shipped, the five behavioural changes to read before deploying (notably that hexisd now refuses to start without a token and that execute requires Nexus liveness), what is blocked versus doable now, and the limitations accepted along the way. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Uea55zaiWuEByEDC4UBSdd --- HANDOFF.md | 132 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 HANDOFF.md diff --git a/HANDOFF.md b/HANDOFF.md new file mode 100644 index 0000000..50dde94 --- /dev/null +++ b/HANDOFF.md @@ -0,0 +1,132 @@ +# Hexis — handoff + +Date: 2026-07-31 +State: all code work from `REVIEW-2026-07-30.md` is **committed and pushed**; **nothing is deployed**. +Branch `master`, remote `origin` = `ssh://git@192.168.1.104:2222/kami/hexis`. + +Stopped here deliberately: the deploy is blocked on Maven, which is under +construction. + +--- + +## What shipped + +Eight commits, `945e4ba..ae6bd08`. All eight review findings are closed. + +| commit | what | findings | +|---|---|---| +| `c7325a2` | Auth on `/api/v1/` + server-derived capability guards | 1, 2 | +| `d428560` | Execution audit fields + in-flight guard as a SQL constraint | 4, 7 | +| `47be24c` | Target validation against Nexus | 3 | +| `be08938` | `context` through `Provider.Execute`; read-only timeouts | 6 | +| `9e6b995` | Dead code + three tests that assert nothing | 5 | +| `dda4acf` | One capability serializer + `GET /api/v1/executions` | §4.5, wire shapes | +| `3ac0fbb` | gofmt realignment | — | +| `ae6bd08` | Makefile, README, the review itself | — | + +Verified at HEAD: `go build`, `go vet`, `go test ./...`, `go test -race ./...` +all pass; `gofmt -l` clean; a fresh database migrates to the final +`user_version` and reopening is a no-op. + +**Intermediate commits are not individually compile-verified — only HEAD is.** +Several hunks span findings (one 124-line `engine.go` hunk covers both target +validation and a dead-code import removal), so whole hunks were assigned to +their dominant commit rather than hand-split. Concretely, `c7325a2` also +carries the `listCapabilities` serializer change and registers the +`/executions` route whose handler does not arrive until `dda4acf`. `git bisect` +across the middle of this range may not build. + +## What changed behaviourally (read before deploying) + +1. **`hexisd` refuses to start without `HEXIS_API_TOKEN`.** Fails closed twice: + `log.Fatalf` before listening, and the middleware returns 503 rather than + ever serving unauthenticated. `/health` and `/ready` stay open. +2. **Execute now requires Nexus liveness.** An unreachable Nexus means 503 + `ErrTargetUnverifiable`, not a permitted execution. This dependency did not + exist before. +3. **`enabled` and `requires_confirmation` are no longer accepted from the + request body at all** — both derived from the risk tier server-side. +4. **The 11 → 12 migration writes to existing rows.** Duplicate in-flight rows + are rewritten to `status='unknown'` (lowest `id` per pair kept), because the + new partial UNIQUE index cannot be created otherwise. Check first: + ```sql + SELECT capability_id, target_entity_id, COUNT(*) FROM executions + WHERE status='started' GROUP BY 1,2 HAVING COUNT(*)>1; + ``` +5. Capabilities now execute at all — 14 `read` + 1 `low` enabled without + confirmation, 3 `medium` docker mutations enabled *with* confirmation. + `ssh.exec` remains allowlist-skipped. + +## Next steps, in order + +### 1. Blocked on Maven — the deploy (Vikunja 355) +Cannot proceed while Maven is under construction. When it resumes, these ship +**together** or voice execution breaks: + +- Maven: `go mod vendor` (the vendored client at + `Maven/vendor/github.com/kami/hexis/pkg/client/client.go` predates even + `WithToken`). +- Maven: `cmd/mavend/ecosystem.go:322` → + `hexisclient.New(cfg.Hexis.URL).WithToken(cfg.Hexis.Token)`, plus a `Token` + field on the Hexis config block and the token in deploy config. +- Hexis: `HEXIS_API_TOKEN` in `Maven/deploy/ecosystem/docker-compose.yml`. +- Bring up workspace-mcp (container is `Exited (0)`, nothing on `:9930`) and + confirm Nexus is up on `:9740`. +- Rebuild **from a commit**. The running image was built 2026-07-19 21:14, + ~3.5h before the first commit existed — it has never corresponded to any + revision. `docker-compose` uses `build: ../../../hexis` with no tag or + digest, so `make compose-up` ships whatever is in the working tree. +- Then verify live: a read capability returns `succeeded` with non-empty + `changes`. + +Task 343's serializer change does **not** break Maven — it reads only `.ID`, +`.Name`, `.Description`, `.ReadOnly`, all unchanged, and `id` is still on the +wire. The only break is auth. + +### 2. Not blocked — can be done any time + +- **Vikunja 354** — `Maven/deploy/ecosystem/nginx.conf:32-42` is a template + with a wildcard `listen 80;` and no ACL. The *live* config is correctly + locked to WireGuard + LAN with `deny all`, but anyone following the repo's + own instructions would produce the internet-exposed config the review + described. Also: public DNS for `hexis./maven./nexus./vikunja..kvmx.ru` + resolves to `109.229.102.117`, which is not this host and is unreachable — + stale records, worth cleaning up. +- **Vikunja 357 — a decision, not code.** Blessing is specified in + `ECOSYSTEM-SPEC.md` (§1.2, §4.3, §7.1, §8 Phase 5, §10 invariant 7) and + implemented nowhere in Nexus: no column, no migration, no TODO, no tracker + item. What shipped in `47be24c` stops free-text targets but does **not** + stop mutation of an auto-discovered entity, which is what blessing exists to + prevent. Either blessing lands in Nexus, or the spec gets amended to describe + the guard that actually exists. Right now the spec documents a protection + that is absent everywhere. + +### 3. Latent — a tripwire, not a bug (Vikunja 358) + +`GET /api/v1/executions?since=` pages on the executions row's implicit SQLite +`rowid`. Safe **only** while nothing deletes execution rows and nothing runs +`VACUUM`; either renumbers and silently invalidates outstanding cursors, so a +polling client would skip or repeat rows with no error. The table grows +unboundedly, so retention will eventually be wanted — add an explicit monotonic +`seq` column **first**. Documented at the `executionColumns` declaration in +`internal/storage/sqlite.go`. + +## Known limitations accepted + +- Rows predating the 11 → 12 migration get `capability_version = 0`, + indistinguishable from a genuine 0. Backfill is impossible — the capabilities + have since mutated and the executions never recorded what they ran against. +- Both `id` and `capability_id` are emitted, carrying the same value. Kept + deliberately so Maven does not take a second breaking change alongside auth. + Retiring `id` is an announced removal, not a tidy-up; a test says so. +- `schema_migrations` was **not** dropped despite the review's remove list. + `migrate()` applies migrations by slice index and writes + `user_version = index + 1`, so removing an element renumbers every later + migration and any database past that point would permanently skip one. The + `CREATE TABLE` is now a no-op holding its slot. + +## One thing to reconsider + +`REVIEW-2026-07-30.md` is committed in-tree. It documents the unauthenticated +execute attack path and names `hexis.kvmx.ru`. Fine on a LAN Gitea; reconsider +before this repo is mirrored anywhere wider.