Add a Makefile, a README and the review this branch works from

There was no onboarding doc of any kind. The Makefile covers build, test, vet,
fmt, a fmt-check gate, tidy, run, the MCP stdio mode, docker-build and the
ecosystem compose targets. The README covers what Hexis is and where it sits
between Nexus, Praxis and workspace-mcp, how to build and run it, the flag and
environment table including the new HEXIS_API_TOKEN, the API surface, and the
tests.

REVIEW-2026-07-30.md is the engineering review the preceding commits address,
kept in-tree as the rationale for them. It includes an independent
second-reviewer pass, and its "uncertainties" section has since been resolved:
the service is not reachable from the public internet (the internet-exposed
nginx config in the Maven repo is a template, not what is deployed), Nexus has
no blessing concept and none is planned, and the running image was built from
an uncommitted working tree hours before the first commit existed — which is
why the deployed binary never matched any revision.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uea55zaiWuEByEDC4UBSdd
This commit is contained in:
kami
2026-07-30 23:40:45 +04:00
parent 3ac0fbb6d7
commit ae6bd08ad8
3 changed files with 454 additions and 0 deletions
+73
View File
@@ -0,0 +1,73 @@
# Hexis — capability registry + guarded execution.
# Pure-Go build (modernc sqlite), so CGO is never required.
GO ?= go
BIN_DIR ?= bin
IMAGE ?= hexis:dev
DATA_DIR ?= $(HOME)/.local/share/hexis
HTTP_ADDR ?= localhost:9741
.DEFAULT_GOAL := build
.PHONY: help
help: ## List targets
@grep -hE '^[a-zA-Z_-]+:.*?## ' $(MAKEFILE_LIST) | \
awk -F':.*?## ' '{printf " %-14s %s\n", $$1, $$2}'
.PHONY: build
build: ## Build hexisd and hexisctl into bin/
CGO_ENABLED=0 $(GO) build -trimpath -o $(BIN_DIR)/hexisd ./cmd/hexisd
CGO_ENABLED=0 $(GO) build -trimpath -o $(BIN_DIR)/hexisctl ./cmd/hexisctl
.PHONY: test
test: ## Run the test suite
$(GO) test ./...
.PHONY: vet
vet: ## Run go vet
$(GO) vet ./...
.PHONY: fmt
fmt: ## Format all Go sources
$(GO) fmt ./...
.PHONY: fmt-check
fmt-check: ## Fail if any Go source is unformatted
@out="$$(gofmt -l .)"; \
if [ -n "$$out" ]; then echo "unformatted files:"; echo "$$out"; exit 1; fi
.PHONY: check
check: fmt-check vet test ## fmt-check + vet + test
.PHONY: tidy
tidy: ## Tidy go.mod/go.sum
$(GO) mod tidy
.PHONY: run
run: build ## Run hexisd locally (HTTP API)
$(BIN_DIR)/hexisd -http $(HTTP_ADDR) -data $(DATA_DIR)
.PHONY: run-mcp
run-mcp: build ## Run hexisd as an MCP stdio server
$(BIN_DIR)/hexisd -mcp -data $(DATA_DIR)
.PHONY: clean
clean: ## Remove build output
rm -rf $(BIN_DIR)
.PHONY: docker-build
docker-build: ## Build the distroless container image
docker build -t $(IMAGE) .
# The compose file that actually runs Hexis lives with the ecosystem stack, not
# in this repo. Override COMPOSE_DIR if yours is elsewhere.
COMPOSE_DIR ?= ../Maven/deploy/ecosystem
.PHONY: compose-up
compose-up: ## Build + start the ecosystem stack (nexus, praxis, hexis)
docker compose -f $(COMPOSE_DIR)/docker-compose.yml up -d --build hexis
.PHONY: compose-logs
compose-logs: ## Tail hexis logs from the ecosystem stack
docker compose -f $(COMPOSE_DIR)/docker-compose.yml logs -f hexis
+204
View File
@@ -0,0 +1,204 @@
# Hexis
Capability registry and **guarded execution** service for the Nexus / Praxis / Hexis
ecosystem (see `ECOSYSTEM-SPEC.md` §4 in the sibling repo root).
Hexis is the only component allowed to *act* on infrastructure. It holds a registry
of named capabilities (`workspace.docker.restart`, …), each bound to a registered
provider implementation — never to a shell string — and mediates every execution
behind a set of guards:
- a capability must be `enabled`;
- capabilities marked `requires_confirmation` need a valid, unexpired confirmation
matching the capability, target and argument hash (default TTL applies);
- one in-flight execution per `(capability, target_entity_id)` — a second attempt
gets `409`;
- a wall-clock timeout per capability; on timeout the outcome is `unknown`
(never `failed`, never auto-retried).
Its place in the ecosystem:
- **Nexus** (`:9740`) owns entities. Hexis resolves free-text targets to entity IDs
through Nexus rather than inventing them.
- **Praxis** (`:8989`) owns items/incidents and consumes execution correlation.
- **Hexis** (`:9741`) owns capabilities, confirmations and executions.
- Consumers today: Maven (voice + mavweb) over the HTTP API, plus MCP clients via
the stdio adapter.
Actual work is performed by providers. The live one is **workspace_mcp**, which
proxies an allowlisted set of tools from a Workspace MCP HTTP server (docker,
filesystem, …). There is also a `systemd` provider stub, which no capability
currently registers.
> Status note: this repo has a candid engineering review in
> `REVIEW-2026-07-30.md`. Read it before trusting the guards — several are
> documented there as incomplete at the time of writing.
## Build
Go 1.25+. No CGO (sqlite is the pure-Go `modernc.org/sqlite`).
```sh
make build # -> bin/hexisd, bin/hexisctl
make check # gofmt check + go vet + go test
```
Individual targets: `make test`, `make vet`, `make fmt`, `make fmt-check`,
`make tidy`, `make clean`, `make help`.
## Run locally
```sh
export HEXIS_API_TOKEN=dev-token # required; hexisd refuses to start without it
make run # bin/hexisd -http localhost:9741 -data ~/.local/share/hexis
```
That runs registry + execution API with no workspace provider, so nothing is
executable — useful for API work. To wire the real provider:
```sh
bin/hexisd \
-http localhost:9741 \
-data ~/.local/share/hexis \
-workspace-url http://localhost:9930 \
-workspace-allowlist etc/workspace-allowlist.yaml
```
On startup Hexis discovers the workspace tools and registers a capability for each
allowlisted entry. Discovery failure is a warning, not fatal; a missing or
unreadable allowlist file **is** fatal when `-workspace-url` is set.
MCP stdio mode (same storage, same engine, tools `hexis.list_capabilities`,
`hexis.inspect_capability`, `hexis.resolve_target`, `hexis.execute`,
`hexis.execution_status`):
```sh
make run-mcp # bin/hexisd -mcp -data ~/.local/share/hexis
```
`bin/hexisctl` is a thin CLI over the HTTP API:
```sh
bin/hexisctl health
bin/hexisctl capability list [--entity ENTITY_ID]
bin/hexisctl capability show <id>
bin/hexisctl exec <capability-id> <target-entity-id> [--args JSON] [--idempotency KEY]
bin/hexisctl execution <id>
```
## Configuration
Flags (all optional) on `hexisd`:
| Flag | Default | Meaning |
| --- | --- | --- |
| `-http` | `localhost:9741` | HTTP listen address |
| `-data` | `$HOME/.local/share/hexis` | Data dir; SQLite lives at `<data>/hexis.db` |
| `-mcp` | `false` | Run the MCP stdio adapter instead of the HTTP server |
| `-workspace-url` | `$WORKSPACE_MCP_URL` | Workspace MCP HTTP API URL, e.g. `http://localhost:9930`. Empty ⇒ provider disabled |
| `-workspace-allowlist` | `<data>/workspace-allowlist.yaml` | Tool allowlist YAML (repo copy: `etc/workspace-allowlist.yaml`) |
| `-nexus` | `$HEXIS_NEXUS_URL`, else `http://localhost:9740` | Nexus base URL, used by `hexis.resolve_target` |
Environment:
- `WORKSPACE_MCP_URL` — fallback for `-workspace-url`.
- `HEXIS_NEXUS_URL` — fallback for `-nexus`.
- `HEXIS_URL` — read by `hexisctl` only; defaults to `http://localhost:9741`.
- `HEXIS_API_TOKEN` — shared bearer token; required by `hexisd`, and also read by
`hexisctl` (and by `pkg/client` via `WithToken`) to authenticate its calls.
**Authentication.** The whole `/api/v1/` surface sits behind a shared bearer
token read from `HEXIS_API_TOKEN`; `/health` and `/ready` stay open for probes.
It fails closed: `hexisd` refuses to start without the variable set, and the
middleware answers `503` rather than serving unauthenticated if it is somehow
empty. Callers send `Authorization: Bearer <token>`; a missing or wrong token
gets `401`.
```sh
export HEXIS_API_TOKEN="$(openssl rand -hex 32)"
curl -H "Authorization: Bearer $HEXIS_API_TOKEN" localhost:9741/api/v1/capabilities
```
(This landed while the README was being written — if the details drift, `internal/api/handler.go`
`requireAuth` is the source of truth.)
Ports: Hexis `9741`, Nexus `9740`, Praxis `8989`, Workspace MCP `9930`.
## API surface
All `/api/v1/` responses are JSON. A request may send `X-Hexis-Version`; if
present it must be `v1`, otherwise the request is rejected with `412`. Omitting
the header is allowed.
| Method | Path | Notes |
| --- | --- | --- |
| `GET` | `/health` | liveness |
| `GET` | `/ready` | readiness |
| `GET` | `/api/v1/capabilities` | optional `?entity_id=` filter |
| `POST` | `/api/v1/capabilities` | register a capability |
| `GET` | `/api/v1/capabilities/{id}` | |
| `DELETE` | `/api/v1/capabilities/{id}` | |
| `POST` | `/api/v1/confirmations` | `{capability_id, target_entity_id, arguments?, requester?}``201` with the confirmation |
| `POST` | `/api/v1/execute` | `{capability_id, target_entity_id, arguments?, confirmation_id?, …}`; also reads `X-Correlation-ID` / `X-Causation-ID` |
| `GET` | `/api/v1/executions` | history; `?entity_id=`, `?since=<seq>`, `?limit=` (≤100) |
| `GET` | `/api/v1/executions/{id}` | |
| `GET` | `/api/v1/changes` | event feed, `?since=<sequence>`, up to 100 events |
Execute status codes: `409` in-flight duplicate, `404` unknown capability, `403`
confirmation/enablement failures, `400` otherwise.
### Pagination
`/api/v1/executions` and `/api/v1/changes` share one cursor convention: `since`
is an **exclusive integer sequence**, results come back in ascending order, and
you page by passing the sequence of the last item you saw. Executions carry it
as `seq`, events as `sequence`. A full page means "call again"; there is no
separate next-page token, and there is no timestamp cursor.
### Capability wire shape
Every producer — the HTTP handler, the MCP adapter and `pkg/client` — serializes
a capability through the single shape defined in `pkg/client/capability.go`,
converted by `internal/wire`. Notable points:
- `capability_id` (the spec §4.1 name) and `id` are **both** emitted and always
carry the same value. `id` is a deprecated alias retained for Maven's client;
see the compatibility note in `pkg/client/capability.go` before removing it.
- `enabled` and `requires_confirmation` are always present, including when
false. They are derived server-side from the risk tier and are never settable
by a caller.
## Tests
```sh
make test # go test ./...
go test ./internal/execution/ -run TestConfirmation -v
```
Tests are table-driven Go tests colocated with their packages — currently
`internal/api/handler_test.go`, `internal/execution/{engine,contract}_test.go`,
`internal/provider/workspace_mcp_test.go`. They use temporary SQLite files and
need no running services.
## Container and deploy
```sh
make docker-build # distroless image, static CGO_ENABLED=0 binary
```
The image entrypoint is `/hexisd -data /data -http 0.0.0.0:9741`, so the data
volume must be mounted at `/data`, `HEXIS_API_TOKEN` must be in the container
environment, and — when `WORKSPACE_MCP_URL` is set — the allowlist must be present
at `/data/workspace-allowlist.yaml`, or the container exits immediately.
Hexis is deployed as part of the ecosystem compose stack, which lives outside this
repo (`Maven/deploy/ecosystem/docker-compose.yml`, alongside nexus and praxis;
host nginx fronts it). Convenience wrappers:
```sh
make compose-up # build + start just the hexis service
make compose-logs
```
Override `COMPOSE_DIR` if your checkout layout differs from
`../Maven/deploy/ecosystem`.
+177
View File
@@ -0,0 +1,177 @@
# Hexis — Senior Engineering Review
Date: 2026-07-30
Reviewed at: commit `945e4ba` (working tree had one uncommitted change in `cmd/hexisd/main.go`: default Nexus URL `8987``9740`)
Reference contract: `/home/kami/apps/ECOSYSTEM-SPEC.md` §4
---
## verdict
**Fragile + misaligned, but structurally sound and worth repairing incrementally.** No rewrite. The architecture matches the spec's shape; the *guards the spec exists to enforce* are missing, and the primary execution path is functionally dead at HEAD.
---
## first assessment
```text
purpose: capability registry + guarded execution for the Nexus/Praxis/Hexis
ecosystem (ECOSYSTEM-SPEC.md §4)
intended users: Maven (voice + mavweb), Command Center, MCP clients
actual users: Maven only — mavweb reads /api/v1/capabilities; mavend/voice.go:1329-1381
resolves→matches→executes via a *vendored* copy of pkg/client
critical workflows: 1. list capabilities for an entity 2. confirm 3. execute 4. poll changes
current state: running on :9741 — but the live binary predates commit 89d8433
(its GET capability response has no `enabled`/`requires_confirmation`
fields), so deployed ≠ HEAD
known failures: every workspace_mcp capability is registered with enabled=false (verified)
maintenance burden: low — 3.6k LOC, 1 real dependency + pure-Go sqlite, builds clean, vet clean
technical constraints: single-user homesrv, single SQLite writer, distroless container
personal constraints: solo maintainer; spec is the contract, three services must agree
what still works well: confirmation lifecycle (args_hash + version + TTL) is correct and
genuinely well tested; storage layer is honest; timeout⇒unknown is right
what has become obsolete: systemd provider (no systemctl in distroless), ListenUnix, the
contract_test.go "bug documentation" tests, schema_migrations table
```
Classification: **fragile** (safety guards absent), **misaligned** (deployed ≠ HEAD; spec §4.3/§4.4 unmet), partly **underbuilt** (no auth, no arg validation).
---
## main findings
### 1. Every workspace capability is registered disabled — the product's only working provider cannot execute
- **problem:** `BuildCapabilities()` never sets `Enabled`, so it defaults to `false`.
- **evidence:** `internal/provider/workspace_mcp.go:235-249` — the `domain.Capability` literal omits both `Enabled` and `TimeoutSeconds`, leaving them zero-valued. Note that the same function *skips* allowlist-disabled tools at `:226`, so it plainly intends everything it emits to be live: this is an omitted field, not a policy decision. A throwaway test at HEAD printed `Enabled=false RequiresConfirmation=false TimeoutSeconds=0`. `internal/execution/engine.go:41` then returns `ErrCapabilityDisabled` → 403 for all 19 capabilities.
- **impact:** Maven's voice execute path can never succeed against a fresh database. Nothing in the suite catches it — no test executes a workspace-provided capability end to end.
- **action:** **repair.** Set `Enabled` from risk tier and `TimeoutSeconds` explicitly. Must not land alone — see finding 2.
### 2. Fixing (1) naively arms unconfirmed docker stop/restart on a publicly-proxied, unauthenticated port
- **problem:** `BuildCapabilities` also leaves `RequiresConfirmation=false` for `risk: medium` docker start/stop/restart, and the HTTP API has no authentication whatsoever.
- **evidence:** `Maven/deploy/ecosystem/nginx.conf:32-42` proxies `hexis.kvmx.ru:80``127.0.0.1:9741` with no auth block; `internal/api/handler.go:37` exposes `POST /api/v1/execute`; `handler.go:115-166` lets any caller `POST /api/v1/capabilities` with `provider`+`operation` of their choosing. Precisely: `handler.go:142` derives the correct spec §4.3 default (`enabled := req.Risk != "destructive"`), and then `:143-145` lets an optional `enabled` field in the request body override it. The guard is implemented and then made opt-out by the untrusted caller — worse than simply absent, because it reads as enforced. `requires_confirmation` (`:159`) is copied from the body with no derivation at all.
- **concrete attack path:** `POST http://hexis.kvmx.ru/api/v1/capabilities {"name":"x","provider":"workspace_mcp","operation":"ssh.exec","enabled":true}` then `POST /api/v1/execute`. The allowlist blocks that specific one (`workspace_mcp.go:129-135` re-checks `ssh.exec` is disabled) — but `docker.stop_container` is allowed, and self-registration bypasses `requires_confirmation` entirely.
- **impact:** remote unauthenticated container control.
- **action:** **security, blocking.** Require a shared token on `/api/v1/`; derive `requires_confirmation` from risk tier server-side; and stop honouring `enabled` from the request body entirely rather than treating it as an override.
### 3. Hexis accepts free-text targets — the one thing the spec says it must never do
- **problem:** `target_entity_id` is accepted as any non-empty string. Nothing checks the `ent_` shape, existence in Nexus, or `capability.TargetTypes`.
- **evidence:** `handler.go:213-216` checks non-empty only; `engine.go:44-46` only compares against a *pinned* `TargetEntityID`, which is `""` for all 19 registered capabilities. Spec §4.3: "Hexis never accepts a free-text target. Ever."
- **impact:** the invariant "Maven must not invent mappings" is unenforced at the point it matters.
- **action:** **repair.** Validate the target against Nexus `GET /api/v1/entities/{id}` (that endpoint exists) plus a `TargetTypes` match, before creating the execution.
- **note:** the spec's stronger *blessing* guard (§4.3) is **not implementable today**`grep -rni bless` across `nexus/` returns nothing. Nexus has no blessing concept. That's a cross-service gap, not a Hexis bug.
### 4. Audit fields are written but never read back, or never written at all
- **problem:** `confirmation_id` is INSERTed but absent from every SELECT, so it always reads back empty. `causation_id` isn't in the `executions` schema at all, yet `engine.go:79` sets it on the struct — the field is populated in memory with nowhere to land.
- **evidence:** `confirmation_id` appears in the INSERT at `sqlite.go:295` and in **none** of the three executions SELECTs (`:313`, `:369`, `:418`). `causation_id` exists only on `hexis_events` (`:119`, `:502`), never on `executions`. Also missing vs spec §4.1: `capability_version` on executions (it exists on `confirmations` at `:134`, so the precedent is there).
- **impact:** "which confirmation authorized this destructive act" is unanswerable from the API — the core audit question. Silent, not a crash.
- **action:** **repair.** Add the columns as a migration, persist and select them.
### 5. `contract_test.go` asserts the opposite of current behavior and passes anyway
- **problem:** three tests are prose, not verification. `TestContract_ChangesSinceAlwaysZero` documents a bug that commit `74b19e0` already fixed, and can only `t.Logf`. `TestContract_TimeoutGoroutineNotCancelled` is a bare `t.Log`. `TestContract_DestructiveCapabilityDisabledByDefault` re-implements `risk != "destructive"` inside the test and asserts on its own local variable — it would pass if the handler were deleted.
- **evidence:** `internal/execution/contract_test.go:212-299`.
- **impact:** the suite's green status overstates coverage; a misnamed test actively misleads about a fixed bug.
- **action:** **cleanup.** Delete the three; the real coverage gap is an end-to-end execute over a provider-registered capability.
### 6. Read-only failures are reported as `unknown`, and timed-out provider calls leak
- **problem:** `runWithTimeout` (`engine.go:161-186`) can't cancel — `Provider.Execute` takes no `context`. The goroutine runs to the HTTP client's 60s timeout, outliving the 30s capability timeout.
- **evidence:** a POST of a `read`/`read_only` capability against the live service while workspace-mcp was down (`:9930` → 503) hung past 5s. Per spec §4.3 that lands `outcome=unknown` — "may or may not have completed the side effect" — for a call that by definition has none, and `unknown` is never retried.
- **impact:** read failures are unretryable and indistinguishable from genuinely ambiguous mutations.
- **action:** **refactor.** Add `ctx` to the `Provider` interface (2 implementations), and let `read_only=true` timeouts resolve to `failed`.
### 7. Race: the one-in-flight-per-(capability,target) guard is check-then-insert
- **problem:** `engine.go:56-58` queries, then `engine.go:92` inserts, with no uniqueness constraint between them.
- **evidence:** no partial unique index on `(capability_id, target_entity_id)` where `status='started'`; the existing `idx_executions_inflight` is non-unique (`sqlite.go:561`).
- **impact:** two concurrent requests both proceed. Narrow window, single writer, low likelihood — but the spec states it as a hard guard and the existing test only covers the sequential case.
- **action:** **repair**, via a partial unique index.
### 8. Deployed binary is older than HEAD; registered capabilities never refresh
- **problem:** `cmd/hexisd/main.go:80-83` skips any capability whose ID already exists, so allowlist edits to risk/target_type/enabled never propagate to an existing database.
- **evidence:** live GET returns no `enabled` field → pre-`89d8433` build. Rows dated 2026-07-19.
- **impact:** config drift; even the correct fix for (1) won't reach the running instance's existing rows.
- **action:** **repair.** Reconcile on startup (update when the derived record differs), and redeploy.
---
## keep
Confirmation lifecycle and its tests; the storage layer's explicitness; `timeout ⇒ unknown`; the provider registry (2 real implementations justify it); the allowlist-as-config design; `nexusclient`; pure-Go sqlite + distroless build.
## remove
- `internal/provider/systemd.go` — no `systemctl` in the distroless image and no capability ever registers it; it's the spec's §4.2 `systemd_dbus` slot filled with a shell-adjacent stub. Delete, or replace with D-Bus when a real need appears.
- `Server.ListenUnix` (dead; never called).
- `schema_migrations` table (unused; `PRAGMA user_version` is the real mechanism). **Caveat:** confirm no already-deployed database depends on it before dropping — finding 8 establishes the live instance is several commits behind, so its schema state is not HEAD's.
- `execution.ResolveRequest` / `ResolveResult` + `MarshalJSON` (superseded by `nexusclient`).
- `Engine.emitEvent`, `Registry.List`, `DiscoveredTools`, `Capability.IsDestructive`, `ifString`.
- `EventCapabilityUnavailable`, `EventExecutionDenied`, `ExecutionDenied`, and 8 unused `Err*` values.
- The three vacuous contract tests.
## repair now
1. Auth on `/api/v1/` + server-derived `requires_confirmation` (finding 2) — **before anything else**.
2. `Enabled` / `TimeoutSeconds` / `RequiresConfirmation` in `BuildCapabilities`, plus startup reconciliation (1, 8).
3. Target validation against Nexus + `TargetTypes` (3).
4. Persist/read `confirmation_id`, `causation_id`, `capability_version` (4).
5. Partial unique index for in-flight (7).
6. Delete the dead code and misleading tests (5, remove).
## refactor later
- `context` through `Provider.Execute` (6).
- Collapse the four competing wire shapes for a capability — `handler.go:96-108` (which emits both `id` and `capability_id`), `adapter.go:147-157`, `pkg/client`, and Maven's vendored copy — into one serializer.
- `GET /api/v1/executions?entity_id=&since=` (spec §4.5, Command Center needs it).
- A `Makefile` and a README: there is no onboarding doc at all.
## rewrite only if
Nexus gains blessing and versioned capabilities `(capability_id, version)` as the primary key, *and* multi-writer access becomes real. Neither is true; incremental repair is clearly cheaper. The confirmation logic is the hidden knowledge here and it is correct — do not throw it away.
## verification plan
- `go build ./... && go vet ./... && go test ./...` — all currently pass. `gofmt -l` flags `internal/provider/systemd.go`, `internal/provider/workspace_mcp.go`, `pkg/client/client.go` as unformatted at HEAD.
- New tests, each pinning a finding:
- execute a `BuildCapabilities`-registered capability end-to-end (1)
- unauthenticated `/api/v1/execute` rejected (2)
- non-`ent_` / unknown target rejected (3)
- round-trip `confirmation_id` through `GET /executions/{id}` (4)
- concurrent duplicate execute → exactly one row (7)
- Live: rebuild, redeploy, `curl` a read capability once workspace-mcp is back (it is 503 as of this review), confirm `succeeded` and non-empty `changes`.
## uncertainties
- **Unknown:** whether `hexis.kvmx.ru:80` is actually resolvable from outside — the nginx config was read, external reachability was not tested. The severity of finding 2 depends on this; the missing-auth defect stands either way.
- **Unknown:** whether blessing is planned for Nexus soon, or whether the target-existence check in finding 3 is the intended permanent guard.
- **Assumption:** Maven is the only consumer. Based on a repo-wide grep of `/home/kami/apps`; an out-of-tree MCP client would not appear.
- **Confirmed but unexplained:** the live instance is ~4 commits behind. Unclear whether that is a stalled deploy or deliberate.
---
## second-reviewer verification (2026-07-30)
An independent pass re-checked the load-bearing claims against the code at HEAD rather than accepting them. **The verdict and all eight findings stand.** Confirmed directly:
- **Finding 1** — `workspace_mcp.go:235-249` omits `Enabled` and `TimeoutSeconds`; `engine.go:42` rejects on `!capability.Enabled`. The execution path is dead as described.
- **Finding 2** — no `auth`, `token`, or `Authorization` handling exists anywhere under `internal/api/`. The `hexis.kvmx.ru:80` server block proxies straight to `127.0.0.1:9741` with only `X-Forwarded-*` headers set and no auth directive.
- **Finding 4** — verified by comparing the INSERT column list against all three SELECT column lists; `confirmation_id` is write-only. `causation_id` is absent from the `executions` DDL.
- **Finding 5** — the three tests are as characterised. `TestContract_DestructiveCapabilityDisabledByDefault` computes `enabled := risk != "destructive"` as a local variable and asserts on that, touching no production code. `TestContract_TimeoutGoroutineNotCancelled` is a single `t.Log` with no assertions.
- **Finding 7** — `sqlite.go:143` creates `idx_executions_inflight` as a plain `CREATE INDEX`, not `CREATE UNIQUE INDEX`, and there is no partial unique index anywhere.
- **Finding 8** — `cmd/hexisd/main.go:76-79` `continue`s on any capability whose ID already resolves, so derived changes never reconcile.
- **Verification plan** — `gofmt -l` flags exactly the three files named.
Amendments made above, none of which change a conclusion: finding 2's description of `enabled` was corrected (it is a caller-supplied override of a correct server default, not "forced true"), finding 4's line references were corrected to the actual INSERT/SELECT sites, and the `schema_migrations` removal gained a deployed-schema caveat.
One emphasis difference: finding 4 reads as more serious than "silent, not a crash" suggests. The confirmation lifecycle is the part of this codebase the review rightly calls correct and worth keeping — but with `confirmation_id` unreadable, that correctness cannot be demonstrated after the fact from the API. A guard you cannot audit is hard to trust under incident review, which raises finding 4 close to the priority of the target-validation work in finding 3.
Agreement on sequencing is unqualified; see below.
## note on sequencing
Findings 1 and 2 are coupled. Fixing the dead-capability bug without adding auth and confirmation defaults would turn a broken execution path into a remotely reachable one. They should land together as one coherent change.