Add handoff notes: state, deploy blockers, next steps
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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uea55zaiWuEByEDC4UBSdd
This commit is contained in:
+132
@@ -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.
|
||||||
Reference in New Issue
Block a user