Phase 3 of #273. Container plumbing for the bundled frontend dist:
- flake.nix overlay: `pkgs.hyperhive-frontend` exposed for the
agent / manager containers (mirrors the existing `pkgs.hyperhive`
pattern); module argument `hyperhiveFrontend = system: self
.packages.${system}.frontend` threads the package into the host
hive-c0re module without forcing operators to apply the overlay
on their host pkgs.
- `services.hive-c0re.frontend` option: pinned to the flake's
frontend package by default, overridable for custom dashboard
SPAs. The hive-c0re systemd service gets `HIVE_STATIC_DIR =
${cfg.frontend}/dashboard` — the Rust binary will pick it up
in Phase 4.
- `hyperhive.frontend.dist` option: per-container, defaults to
`pkgs.hyperhive-frontend`. Override to ship a fully custom
agent SPA (advanced; the default + extraFiles flow handles the
common 'add files' case).
- `hyperhive.frontend.extraFiles` option: attrsOf submodule
(mirroring the `hyperhive.extraMcpServers` shape per damocles'
request so existing #322-style assertions keep their grip).
Each entry has `source` (path relative to agent.nix) and
`target` (URL/disk prefix within the merged static tree,
defaulting to the attribute name). Operator-named example:
the bitburner agent drops `bitburner-dist` into
`/bitburner/` alongside the default agent UI at `/`.
- `hyperhive.frontend.mergedDist` (readOnly): the runCommand
derivation that composes `agent/` from the default dist plus
every `extraFiles` entry. Aborts on overwrite so a filename
collision becomes a build error rather than a silent dist swap.
agent-base.nix + manager.nix set their respective systemd
service `HIVE_STATIC_DIR` to this merged path.
Until Phase 4 lands, the env var is set but unused — the Rust
binaries still serve assets via `include_str!`. The cutover
happens in the next commit on this branch.
Refs #273.
Phase 2 of #273. Adds `packages.${system}.frontend` to the flake —
a `buildNpmPackage` derivation that consumes the lockfile committed
in the previous step and produces two static dist trees under $out:
$out/dashboard/ the hive-c0re dashboard SPA assets
(index.html, app.js, dashboard.css, favicon.svg)
$out/agent/ the per-agent default UI assets
(index.html, app.js, stats.html, stats.js,
agent.css, screen.html)
The dashboard favicon lives outside the frontend src tree
(branding/hyperhive.svg at the repo root). It's passed in as a
callPackage argument so the hermetic build can grab it.
`npmDepsHash` is set to `lib.fakeHash` — the build will fail on
first attempt with the actual sha256 printed; copy that in. Use
`nix run nixpkgs#prefetch-npm-deps -- frontend/package-lock.json`
to recompute locally without a build round-trip (works from
operator's host; iris's container can't recompute it without
prefetch-npm-deps in PATH).
The Rust crates and NixOS modules continue to use the legacy
include_str! routes; cutover happens in Phase 4.
Refs #273.
new `services.hive-forge.enable` (off by default) wraps
`services.forgejo` with hyperhive-friendly defaults: sqlite (no
extra db service), built-in ssh on 2222 so it doesn't fight the
host's openssh, http on 3000 (outside hyperhive's 7000/8000/8100-8999
ranges), registration off (operator seeds agent users), private
repos by default. exported as `nixosModules.hive-forge` — operator
imports it on the host alongside hive-c0re. container-side wiring
(MCP tools or a bind-mounted token) is deferred; containers already
share the host netns so they can reach http://localhost:3000 today.
revert the earlier 'operator must set allowUnfree' move:
per-agent containers evaluate their own nixpkgs and the operator's
host-level allowUnfree doesn't propagate in. restoring the scoped
allowUnfreePredicate inside both the claude-unstable overlay and
harness-base.nix; documented in README + gotchas as 'nothing to
set on the operator side'.
docs:
- claude.md file map adds crash_watch.rs, kick_agent on coordinator,
/api/model + journald viewer + bind-with-retry references.
- scratchpad rewritten to reflect the recent run.
- web-ui.md: notification row + browser notifications section,
state row (badge + model chip + last-turn chip + cancel button),
per-agent inbox, /model slash, /cancel-question + journald
endpoints, focus-preservation on refresh.
- turn-loop.md: --model is read from Bus::model() per turn (runtime
override via /model); recv(wait_seconds) up to 180s with the
rationale; ask_operator gains ttl_seconds; new TurnState section;
kick_agent inbox-on-startup hint.
- approvals.md: ttl/cancel resolution paths for operator questions.
- persistence.md: /state/hyperhive-model file.
- gotchas.md: web UI port collision policy (rename, don't probe);
bind retry + SO_REUSEADDR shape; auto-unfree restored.
- todo.md: cleaned up empty sections and stale entries; /model
shipped, dropped from the list.
model persistence: /model <name> now writes to /state/hyperhive-model
(in-container), Bus::new reads it on init. operator override survives
harness restart and container rebuild; gone on --purge like every
other piece of agent state. path overridable via HYPERHIVE_MODEL_FILE
for tests. failure to persist is a warn, not fatal — runtime override
still applies, just won't survive a restart.
unfree opt-in: drop the auto-allowUnfreePredicate from
harness-base.nix and the claude-unstable overlay. operator now has to
set nixpkgs.config.allowUnfree (or a predicate listing claude-code)
in their own host config. silent unfree bypass was sketchy; this is
honest. readme + gotchas updated to spell out the snippet.
todo: drops model-persistence + container-crash + journald (all
shipped); adds per-agent send allow-list (constrain who an agent can
message).