kami 66f5c397f7 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
2026-07-31 00:46:43 +04:00

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).

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

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:

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):

make run-mcp   # bin/hexisd -mcp -data ~/.local/share/hexis

bin/hexisctl is a thin CLI over the HTTP API:

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.

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

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

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:

make compose-up            # build + start just the hexis service
make compose-logs

Override COMPOSE_DIR if your checkout layout differs from ../Maven/deploy/ecosystem.

S
Description
No description provided
Readme 126 KiB
Languages
Go 98.6%
Makefile 1.1%
Dockerfile 0.3%