hyperhive/docs/conventions.md
iris 5ca96b8c85 docs: scrub self-ref cookies across 5 files (#719 batch 2)
All five files scrubbed to 0 issue-ref cookies. Most refs were
'(closes #NNN)' style attribution after closed work landed.

- docs/turn-loop.md: 6 → 0 (#598 unified binary, #419 auth-fail,
  #542 mtime resumption, #519 role markers, #442 spawn path,
  #474/#478 partial edit)
- docs/approvals.md: 5 → 0 (#317 flake validation, #250 withdraw,
  #441 rebuild-queue dispatch, #753 manager port hash, #425
  crash-watch grace)
- docs/terminal-rendering.md: 2 → 0 (#666 ask-operator inline)
- docs/conventions.md: 3 → 0 (#753 port hash, #692 parent
  sentinel, #486 reparenting)
- docs/security.md: 4 → 0 (#240 + #658 ×2 + #673/#678 attribution)

Combined with PR #810 (docs/web-ui.md 14→0), this PR closes the
small / medium files. Remaining: agent-hierarchy.md (13),
gotchas.md (13), persistence.md (13). gateway.md is atlas's lane.
2026-05-31 15:39:09 +02:00

4.3 KiB

Conventions

Code-style and process expectations across the workspace. Most of these exist because something already went wrong without them.

Naming

  • Containers are length-bounded by nixos-container (≤ 11 chars).
  • Sub-agents are h-<name> with <name> ≤ 9 chars.
  • The manager is hm1nd (no h- prefix, fixed name).
  • MAX_AGENT_NAME in lifecycle.rs enforces the cap.
  • Per-agent web UI port = WEB_PORT_BASE + FNV1a(name) % WEB_PORT_RANGE (8100..8999) for every agent including the manager; dashboard cfg.dashboardPort (default 7000).

Identity = socket

There are no auth tokens on the per-agent unix sockets. The socket path identifies the principal; perms come from "who has the bind-mount." A sub-agent only sees its own /run/hive/mcp.sock; the manager has access to its privileged socket; hive-c0re owns the host admin socket.

Recipient sentinels

A few recipient names are reserved by the broker and have special meaning that ordinary agent labels can never collide with — agent name validation rejects any character outside [a-z0-9_-], so the angle-bracket and asterisk shapes below are structurally safe.

  • * — broadcast: deliver to every running agent except the sender (agent_server::handle_send fans out via Coordinator::broadcast_send).
  • operator — the human at the dashboard. Messages accumulate in the inbox view; no agent ever recv's them.
  • <parent> — the sender's parent per topology.json. Rewritten at send time by topology::resolve_recipient: looks up parent_of(sender) and falls back to operator when the sender is a root agent (or absent from topology entirely). Lets agents address their parent without learning the label, so runtime reparenting propagates with zero agent-side restart.

When the resolver rewrites <parent>, the broker stores the resolved label as the message's recipient — the dashboard and recv side both see the real route. The sentinel is purely a send-time addressing convenience.

Wire protocol

JSON line-delimited over unix sockets in both directions (host admin / manager / agent). SSE streams (/dashboard/stream on hive-c0re, /events/stream on the per-agent web UIs) are text/event-stream; each frame carries a seq field for the snapshot-dedupe dance (see docs/web-ui.md). Request/response types live in hive-sh4re — change them in one place. The dashboard event vocabulary lives in hive-c0re::dashboard_events::DashboardEvent.

Async forms

Dashboard + per-agent mutating forms carry data-async; a delegated submit listener in assets/tabs.js intercepts, shows a spinner, POSTs application/x-www-form-urlencoded (axum's Form extractor rejects multipart), calls refreshState() on success. New mutating forms should add data-async and optionally data-confirm (for a JS-side confirm() prompt) or data-prompt="…" (for a window.prompt() whose answer goes into a hidden input named by data-prompt-field, default note).

refreshState defers automatically when document.activeElement sits inside a managed section so the operator's typing isn't lost; collapsible <details data-restore-key=…> survive the re-render via snapshotOpenDetails / restoreOpenDetails.

rebuild is the reconcile verb

lifecycle::rebuild idempotently rewrites /etc/nixos-containers/<C>.conf (PRIVATE_NETWORK=0, clears HOST_ADDRESS / LOCAL_ADDRESS, sets EXTRA_NSPAWN_FLAGS), regenerates applied/<name>/flake.nix, writes the systemd limits drop-in, then nixos-container update + stop + start.

Anything that changes per-container state on the host should be re-applied here so a manual ↻ R3BU1LD from the dashboard is sufficient to recover.

Actions are factored

approve / deny / destroy (and the lifecycle helper) live in actions.rs / dashboard.rs. The admin socket and the dashboard POST handlers both call into them so the two surfaces never drift.

Commit messages

Short, lowercase, no Co-Authored-By trailer. Imperative mood, no period. Body explains why if non-obvious; otherwise the subject alone is fine. Wrap at ~72 cols.

Commit before test

Stage and commit when work looks ready, then run validation (cargo check, nix flake check, real deploy). Failures get a follow-up commit rather than an amend. The commit history is the work log; rewriting it loses signal.