docs(#2627): add READMEs for the remaining infra crates

Second increment of the per-crate README effort, covering the rest of the
infra/wire/priv column: hive-priv, hive-metric, hive-types, hive-sh4re,
hive-core-agent-sock, hive-agent-sock. Same shape as the first batch —
purpose + when-to-use, and point at the crate-root //! docs plus the
relevant docs/ pages rather than duplicating them. Wires
readme = "README.md" into each Cargo.toml [package].

Disjoint from the batch-1 crates, so the two increments compose cleanly.
This commit is contained in:
atlas 2026-07-23 12:37:04 +02:00 committed by mara
commit bbfc578c95
12 changed files with 162 additions and 0 deletions

View file

@ -2,6 +2,7 @@
name = "hive-agent-sock"
edition.workspace = true
version.workspace = true
readme = "README.md"
[lints]
workspace = true

26
hive-agent-sock/README.md Normal file
View file

@ -0,0 +1,26 @@
# hive-agent-sock
Wire types for the **in-agent socket** — the one the `hive-agent` harness serves
*inside* the container to its local producers (the matrix / bash MCP daemons and
`forge_notify`). Unlike the host-served sockets, this one **never leaves the
container**.
## What it carries
- the loose-ends-v2 **todo** op family
- the harness-local **reminder** op family
More in-agent request families may be added over time — the socket is
deliberately named for the agent, not for the todos.
## Why in-container
The harness owns the todo + reminder stores locally and signals its own turn
loop directly, so `hive-c0re` is not in either path: no broker round-trip, no
long-poll, no marker files. That locality is the point — it's also what lets
these stores travel with the agent for hive portability.
## Not to be confused with `hive-core-agent-sock`
That crate is the *host*-served core↔agent protocol on `/run/hive/mcp.sock`
(the harness talking out to `hive-c0re`). This socket is purely intra-container.

View file

@ -2,6 +2,7 @@
name = "hive-core-agent-sock"
edition.workspace = true
version.workspace = true
readme = "README.md"
[lints]
workspace = true

View file

@ -0,0 +1,21 @@
# hive-core-agent-sock
Wire types for the **per-agent + manager socket** (`/run/hive/mcp.sock`) — the
unified `Request` / `Response` protocol spoken between an agent's in-container
harness (and the manager) and the `hive-c0re` daemon.
## Why it's its own crate
Re-homed out of `hive-sh4re` so this one socket owns its protocol crate,
mirroring the `hive-host-sock` / `hive-priv-sock` splits. The shared payload
types it references (`Message`, `LooseEnd`, `Approval`, …) stay in `hive-sh4re`,
which this crate depends on — this crate is just the request/response envelope
for this socket.
## Not to be confused with `hive-agent-sock`
This is the **host-served** protocol: the harness talks *out* to `hive-c0re`
over `/run/hive/mcp.sock` (broker sends, approvals, questions, lifecycle).
`hive-agent-sock` is the separate *in-container* socket the harness serves to
its own local producers — that one never leaves the container. See
`docs/boundary.md` for the socket topology.

View file

@ -1,6 +1,7 @@
[package]
name = "hive-metric"
version.workspace = true
readme = "README.md"
edition.workspace = true
[[bin]]

26
hive-metric/README.md Normal file
View file

@ -0,0 +1,26 @@
# hive-metric
A tiny CLI that pushes **a single labeled metric** to the OTEL collector, then
exits. Built on the OpenTelemetry Rust SDK with the OTLP HTTP/JSON exporter.
## When to use it
For emitting a one-off counter or gauge from a shell script or systemd unit
without embedding an OTEL SDK in the caller — the harness and various hive
scripts use it to record per-agent metrics.
## Usage
```text
hive-metric <name> <value> [--type counter|gauge] [--labels key=value ...]
```
Standard OTEL environment variables are read automatically by the SDK:
- `OTEL_EXPORTER_OTLP_ENDPOINT` — collector URL (**required**)
- `OTEL_EXPORTER_OTLP_HEADERS` — auth headers (`Key=Value,...`)
- `OTEL_RESOURCE_ATTRIBUTES` — resource labels (`k=v,...`)
The harness populates all of these per-agent when
`services.hyperhive.otel.enable = true`. See `docs/observability.md` for the
collector setup and the metrics hyperhive exports.

View file

@ -2,6 +2,7 @@
name = "hive-priv"
edition.workspace = true
version.workspace = true
readme = "README.md"
[lints]
workspace = true

32
hive-priv/README.md Normal file
View file

@ -0,0 +1,32 @@
# hive-priv
The minimal **root privileged-helper** for hive-c0re. It runs as root and
exposes a narrow unix socket at `/run/hive/priv.sock` that accepts `PrivRequest`
JSON lines and performs only the handful of operations that genuinely require
root — bind-mount edits, `nsenter` into a container, btrfs subvolume ops. All
coordination logic (broker, HTTP, scheduling) stays in the *unprivileged*
`hive-c0re` process, which delegates here.
## Why it exists
Privsep. `hive-c0re` runs as the unprivileged `hive-core` user so a bug or a
prompt-injection in the large daemon can't directly wield root. The few root
operations it needs are funnelled through this small, auditable helper instead.
See `docs/boundary.md` and `docs/security.md` for the privilege boundary.
## Security model
- **Strict allowlist.** Every request is validated against a container-name
allowlist before any filesystem or process operation — only names matching the
hive convention (`h-*`, the manager container, known sibling service
containers) are accepted.
- **No pass-through.** Every `PrivRequest` variant maps to a single known
operation; there is no arbitrary-command escape hatch.
- **Socket-activated, always.** systemd binds `/run/hive/priv.sock`
(`SocketGroup=hive-core`, `0660`) and passes the listener as fd 3
(`LISTEN_FDS`); the helper requires this and has no self-bind fallback, so dev
and prod take the identical path and the group grant always holds.
The wire contract (`PrivRequest` / response types) lives in the separate
`hive-priv-sock` crate so this root binary depends on just the protocol shapes,
not the whole daemon-shared crate.

View file

@ -2,6 +2,7 @@
name = "hive-sh4re"
edition.workspace = true
version.workspace = true
readme = "README.md"
[lints]
workspace = true

33
hive-sh4re/README.md Normal file
View file

@ -0,0 +1,33 @@
# hive-sh4re
The **shared wire types** between `hive-c0re` and the in-container harness — the
common vocabulary (`Message`, `Approval`, `LooseEnd`, `HelperEvent`, the
Agent / Manager request + response shapes) that the unix-socket protocols are
built from.
## Where it sits
This is the shared payload crate; the per-socket *protocol envelopes* have been
split into their own smaller crates so specialised binaries don't have to pull
in all of `hive-sh4re`:
- `hive-host-sock` — host admin socket (`hivectl``hive-c0re`)
- `hive-core-agent-sock` — per-agent/manager socket (`/run/hive/mcp.sock`)
- `hive-priv-sock` — the privileged-helper socket
Those crates re-export or reference the payload types that still live here
(`Approval`, `Message`, `LooseEnd`, …).
## Modules
- **`jobs`** — the job-queue wire types (`DagView`) surfaced to the dashboard SSE
stream + hivectl.
- **`wire_time`** — the timestamp convention: wire fields are
`chrono::DateTime<Utc>` (serialized RFC 3339), while sqlite storage + input
args stay unix-epoch `i64`; this module owns the two boundary conversions.
- **`paths`** — well-known on-disk path helpers.
- **`assets`** — resolves bundled runtime asset paths (branding, prompts) under
`HIVE_ASSETS_DIR`.
Agent-name fields are typed as `hive_types::Ident` for serde-validated parsing at
the socket boundary.

View file

@ -2,6 +2,7 @@
name = "hive-types"
edition.workspace = true
version.workspace = true
readme = "README.md"
[lints]
workspace = true

18
hive-types/README.md Normal file
View file

@ -0,0 +1,18 @@
# hive-types
Foundational shared **newtypes** for the hyperhive workspace. A zero-dependency
(bar `serde`) leaf crate, so every wire-type crate (`hive-sh4re`,
`hive-host-sock`, `hive-core-agent-sock`) and both binaries (`hive-c0re`,
`hivectl`) can share them without cross-crate coupling and without growing
`hive-sh4re`.
## What's here
- **`Ident`** — a validated agent-name newtype. Typing agent-name fields as
`Ident` (rather than bare `String`) gets serde-validated parsing **at the
socket boundary for free**: a malformed name is rejected at deserialize time
instead of flowing into a handler that has to re-validate by hand.
The crate is deliberately a leaf with the smallest possible dependency footprint
so anything in the workspace can depend on it. See `docs/conventions.md`
("identity = socket") for how agent identity is modelled across the sockets.