diff --git a/claude-plugins/.claude-plugin/marketplace.json b/claude-plugins/.claude-plugin/marketplace.json index d203fb0c..1a50841f 100644 --- a/claude-plugins/.claude-plugin/marketplace.json +++ b/claude-plugins/.claude-plugin/marketplace.json @@ -1,7 +1,7 @@ { "$schema": "https://anthropic.com/claude-code/marketplace.schema.json", "name": "hyperhive", - "description": "hyperhive-authored Claude Code plugins, shipped hive-wide via the default hyperhive.claudeMarketplaces/claudePlugins nix options (nix/agent-modules/claude-settings.nix). Built as a nix store path and added with `claude plugin marketplace add ` - no forge repo or git remote needed. Add a new plugins// subtree + one entry below to ship another.", + "description": "hyperhive-authored Claude Code plugins, shipped hive-wide via the default services.hyperhive.agent.claudeMarketplaces/claudePlugins nix options (nix/agent-modules/claude-settings.nix). Built as a nix store path and added with `claude plugin marketplace add ` - no forge repo or git remote needed. Add a new plugins// subtree + one entry below to ship another.", "owner": { "name": "hyperhive" }, diff --git a/docs/swarm/ui.md b/docs/swarm/ui.md index dfc57102..45ea7975 100644 --- a/docs/swarm/ui.md +++ b/docs/swarm/ui.md @@ -403,8 +403,8 @@ daemon's socket, so the host that runs the controller is the host that can serve the UI. A hive that merely _uses_ a swarm has nothing to serve. The UI build is `deploy.swarm-controller.ui.package` (defaulted from this -flake's `swarm-ui` package). The older `deploy.swarm-ui.package` and -`swarm.ui.package` still work and warn at evaluation. +flake's `swarm-ui` package). The older `deploy.swarm-ui.package` still works +and warns at evaluation. The UI answers on `services.hyperhive.swarm.domain` and nothing else. It shares that name with the swarm-controller it fronts — one service to a diff --git a/frontend/packages/agent/build.mjs b/frontend/packages/agent/build.mjs index e38c1a8a..ab38dcc3 100644 --- a/frontend/packages/agent/build.mjs +++ b/frontend/packages/agent/build.mjs @@ -17,7 +17,7 @@ // resolved from @hive/shared) // // The in-container Rust binary mounts `dist/` (with per-agent -// `hyperhive.frontend.extraFiles` layered on top) as a +// `services.hyperhive.agent.frontend.extraFiles` layered on top) as a // `tower_http::ServeDir` fallback; the layout above keeps every URL // the HTML references reachable without rewriting paths. diff --git a/frontend/packages/agent/package.json b/frontend/packages/agent/package.json index 3d305811..3055cfb3 100644 --- a/frontend/packages/agent/package.json +++ b/frontend/packages/agent/package.json @@ -2,7 +2,7 @@ "name": "@hive/agent", "version": "0.0.0", "private": true, - "description": "hive-ag3nt per-container web UI. Bundled by esbuild into a static dist; served by the in-container Rust binary at runtime via tower_http::ServeDir. Per-agent additions are layered on top via the hyperhive.frontend.extraFiles nix option.", + "description": "hive-ag3nt per-container web UI. Bundled by esbuild into a static dist; served by the in-container Rust binary at runtime via tower_http::ServeDir. Per-agent additions are layered on top via the services.hyperhive.agent.frontend.extraFiles nix option.", "type": "module", "scripts": { "build": "node ./build.mjs", diff --git a/frontend/packages/agent/src/components/Header.tsx b/frontend/packages/agent/src/components/Header.tsx index 177409fa..54ad3558 100644 --- a/frontend/packages/agent/src/components/Header.tsx +++ b/frontend/packages/agent/src/components/Header.tsx @@ -25,7 +25,7 @@ import { useEffect, useRef } from "preact/hooks"; import type { ComponentChildren } from "preact"; -// Icon 404s when the agent has no `hyperhive.icon` override (no +// Icon 404s when the agent has no `services.hyperhive.agent.icon` override (no // bundled server-side default any more — see // `hive_agent::web_ui::screen::serve_icon`). Ports app.js's // `bindHeaderIconFallback` exactly: fire-and-forget load, swap to diff --git a/frontend/packages/agent/src/components/MetaNav.tsx b/frontend/packages/agent/src/components/MetaNav.tsx index 2e0ff5a3..1211110c 100644 --- a/frontend/packages/agent/src/components/MetaNav.tsx +++ b/frontend/packages/agent/src/components/MetaNav.tsx @@ -2,7 +2,7 @@ // (`@hive/shared/icons.js`'s `LinkIcon` — an emoji glyph can't match a // neighbouring icon's size/weight on any platform, see that file's // comment) in the pills cluster that opens a popover listing -// stats/screen/forge/config + any `hyperhive.dashboardLinks` extras, +// stats/screen/forge/config + any `services.hyperhive.agent.dashboardLinks` extras, // sourced from the backend's `agent_links()` (the single source of // truth — same list also feeds the dashboard card's icon strip) — plus // a `🧭 dashboard` back-link. diff --git a/frontend/packages/agent/src/types.ts b/frontend/packages/agent/src/types.ts index 92eeab09..373bf387 100644 --- a/frontend/packages/agent/src/types.ts +++ b/frontend/packages/agent/src/types.ts @@ -38,7 +38,7 @@ export interface AgentState { // Mirrors `hive_agent::web_ui::state::AgentLink` — one meta-nav entry // in the agent page header (stats/screen/forge/config + any -// `hyperhive.dashboardLinks` extras). `agent_links()` on the backend +// `services.hyperhive.agent.dashboardLinks` extras). `agent_links()` on the backend // is the single source of truth for what links an agent exposes; see // docs/web-ui/agent.md::Header for the kind → URL resolution table. export interface AgentLink { diff --git a/frontend/packages/dashboard/src/agents.js b/frontend/packages/dashboard/src/agents.js index ef15746a..62d4f72e 100644 --- a/frontend/packages/dashboard/src/agents.js +++ b/frontend/packages/dashboard/src/agents.js @@ -663,7 +663,7 @@ export function renderContainers(s) { // `docs/web-ui/dashboard.md::Container row` + `docs/networking/gateway.md::Vhost map`. const gatewayLinks = !!(s && s.gateway_enabled); // Forge public URL: state.forge_public_url (set by the NixOS module - // from services.hyperhive.forge.publicUrl) or null — never guessed + // from services.hyperhive.swarm.forge.publicUrl) or null — never guessed // from ":3000". `null` propagates down to // buildContainerLi/buildAgentMenu, which already hide forge links // rather than emit a possibly-wrong one. diff --git a/frontend/packages/swarm-ui/src/pages/LinkMatrixAccountForm.tsx b/frontend/packages/swarm-ui/src/pages/LinkMatrixAccountForm.tsx index fa1668a4..dbdc3584 100644 --- a/frontend/packages/swarm-ui/src/pages/LinkMatrixAccountForm.tsx +++ b/frontend/packages/swarm-ui/src/pages/LinkMatrixAccountForm.tsx @@ -149,7 +149,7 @@ export function LinkMatrixAccountForm({ pattern={ACCOUNT_PATTERN} title="1-63 chars: letters, digits, underscore, hyphen" // Not "primary" — that reads as naming the hive-provided default - // account (`main`, reserved: see `hyperhive.matrixAccounts`'s own + // account (`main`, reserved: see `services.hyperhive.agent.matrixAccounts`'s own // module doc), which this route can neither create nor touch. // Picking a name with no relationship to that default avoids the // misread entirely, rather than requiring a reader to already know diff --git a/hive-agent-mcp/README.md b/hive-agent-mcp/README.md index f65388bb..381fd590 100644 --- a/hive-agent-mcp/README.md +++ b/hive-agent-mcp/README.md @@ -24,7 +24,7 @@ or their own daemon instead of living here. in-agent socket the `hive-agent` harness serves. - **`client.rs`** — socket client to the hyperhive broker. - **`send_allow.rs`** — enforces the per-agent - `hyperhive.allowedRecipients` allow-list on `send`. + `services.hyperhive.agent.allowedRecipients` allow-list on `send`. - **`paths.rs`** — socket + state path resolution shared with the harness's own `paths.rs` conventions. diff --git a/hive-agent/prompts/system.md b/hive-agent/prompts/system.md index 39655fcc..71042c8d 100644 --- a/hive-agent/prompts/system.md +++ b/hive-agent/prompts/system.md @@ -3,7 +3,7 @@ You are hyperhive agent `{label}` (qualified: `{qualified_label}`){hive_identity Tools (hyperhive surface). Full signature + behavior for each comes from the tool's own MCP description (you already received it via the MCP tool schema) — this is just the map of what exists and which ones are gated, so you know where to look: - **Inbox / messaging** (always available): `mcp__hyperhive__recv`, `mcp__hyperhive__ack_until`, `mcp__hyperhive__send`, `mcp__hyperhive__get_loose_ends`, `mcp__hyperhive__cancel_loose_end`, `mcp__hyperhive__mark_todos_done`, `mcp__hyperhive__remind`, `mcp__hyperhive__set_status`, `mcp__hyperhive__get_agent_meta`. `send` reaches peers on this hive only; **reach the operator over matrix** if you have the matrix MCP tools (`mcp__matrix__send_dm` or `mcp__matrix__send_message`); without them the operator reads your `set_status` line and turn output on the dashboard, and your logs. `send(to: "operator")` is refused. One habit worth internalizing beyond the tool descriptions themselves: prefer ending the turn over repeatedly polling `recv` when idle (only turn-boundaries observe in-container todo wakes — bash-task completions, matrix unread, forge activity — and ending the turn is also your checkpoint). For a large todo backlog (`get_loose_ends` caps at 40 rows), clear reviewed ids in bulk with `mark_todos_done` rather than cancelling one at a time — there's no blind range-clear, only ids you've actually looked at. -- **Extra MCP tools** (some agents only): `mcp____` — agent-specific (matrix client, scraper, db connector, etc.) declared in your `agent.nix` under `hyperhive.extraMcpServers`. First-class tools, already operator-approved at deploy time. +- **Extra MCP tools** (some agents only): `mcp____` — agent-specific (matrix client, scraper, db connector, etc.) declared in your `agent.nix` under `services.hyperhive.agent.extraMcpServers`. First-class tools, already operator-approved at deploy time. Need new packages, env vars, or other NixOS config for yourself? You can't edit your own config directly — open a PR on your `agent-configs/` repo, or contact the operator directly. Config repos live at `/agents/{label}/config/` (read-only inside your container). All changes flow through operator-approved commits. diff --git a/hive-agent/src/web_ui/state.rs b/hive-agent/src/web_ui/state.rs index e86a00b7..3ed37ebe 100644 --- a/hive-agent/src/web_ui/state.rs +++ b/hive-agent/src/web_ui/state.rs @@ -169,8 +169,8 @@ pub(super) struct StateSnapshot { links: Vec, /// Public URL of the forge (e.g. /// `"https://forge.pr1ma.darkest.space"`). Sourced from - /// `HIVE_FORGE_PUBLIC_URL` (set from `services.hyperhive.forge. - /// publicUrl`); `None` when unset. The frontend uses this to build + /// `HIVE_FORGE_PUBLIC_URL` (set from + /// `services.hyperhive.swarm.forge.publicUrl`); `None` when unset. The frontend uses this to build /// forge nav-strip links, and **hides** the forge link entirely /// when absent rather than guessing `:3000`. forge_public_url: Option, diff --git a/hive-screen-mcp/README.md b/hive-screen-mcp/README.md index 48199166..02ec3a37 100644 --- a/hive-screen-mcp/README.md +++ b/hive-screen-mcp/README.md @@ -1,13 +1,13 @@ # hive-screen-mcp A stdio MCP server that exposes screenshot, keyboard, and mouse tools to agents -running a Weston Wayland compositor (`hyperhive.gui.enable = true`). +running a Weston Wayland compositor (`services.hyperhive.agent.gui.enable = true`). ## When to use it This crate is the bridge for **GUI agents** — agents that drive graphical applications (browsers, desktop tools, game UIs) and need to see and interact -with a Weston Wayland display. If your agent does not have `hyperhive.gui.enable +with a Weston Wayland display. If your agent does not have `services.hyperhive.agent.gui.enable = true`, none of these tools will function. ## Tools @@ -27,7 +27,7 @@ module; `HIVE_GUI_VNC_PORT` is set by the harness service (defaults to `5900`). ## Shape One binary (`hive-screen-mcp`) launched over stdio. The agent runtime wires it -as an extra MCP server when `hyperhive.gui.enable = true`. There are no library +as an extra MCP server when `services.hyperhive.agent.gui.enable = true`. There are no library exports — it is a self-contained binary crate. See the crate-root `//!` docs for the full per-tool implementation notes and diff --git a/nix/agent-modules/default.nix b/nix/agent-modules/default.nix index 1f5a9197..6af1e2fd 100644 --- a/nix/agent-modules/default.nix +++ b/nix/agent-modules/default.nix @@ -19,23 +19,6 @@ ... }: let - # What an `agent.nix` still setting the dropped per-agent matrix switch is - # told. Shared by the two `mkRemovedOptionModule` entries below, one per - # spelling of the path. - matrixEnableRemoved = '' - The per-agent matrix switch is gone. Matrix is enabled for an agent exactly - when it has at least one services.hyperhive.agent.matrixAccounts entry, and - the hive-internal `main` entry is declared for you whenever - services.hyperhive.agent.matrix.url is non-null. - - - `enable = true` was the default: drop the line, nothing else to do. - - `enable = false`: set services.hyperhive.agent.matrix.url = null instead - (and declare no accounts of your own). That is what leaves the account set - empty, which is now what suppresses the daemon, its path watcher and the - injected extraMcpServers.matrix entry. - - See docs/tools/matrix.md. - ''; hiveNameRemoved = '' An agent reads its hive's name at runtime from /etc/hyperhive-hive-facts, which the hive writes into every container, so no agent build sets it. @@ -71,36 +54,12 @@ in ./packages.nix ./queue.nix ./queue-identity.nix - ./renamed-options.nix ./user.nix ./screen.nix ./weston-vnc.nix - (lib.mkRemovedOptionModule [ "hyperhive" "web" "useUnixSocket" ] '' - Unix socket mode is always enabled for all agents. Remove the - setting from your agent.nix. - '') - (lib.mkRemovedOptionModule [ "hyperhive" "allowedBashPatterns" ] '' - The built-in Bash tool is fully disabled; agents use - mcp__bash__run instead. Remove the setting from your agent.nix. - '') - # The dropped per-agent matrix switch. A removal, not a silent delete, - # because the definition that *changes meaning* is `false`: left undeclared - # it would be ignored and the agent would quietly gain the matrix tools its - # operator turned off. Failing the eval with the replacement spelling is the - # only outcome that cannot do that. - # - # Both spellings, for the same reason ../host-modules/deploy.nix lists both - # of `swarm|deploy.matrix.registrationTokenFile`: an existing agent.nix may - # carry either the `hyperhive.*` path or the `services.hyperhive.agent.*` - # one, and only the one it actually carries will fire. Neither is an entry - # in ./renamed-options.nix — a rename pointing at an undeclared option - # breaks evaluation for everyone, removed or not. - (lib.mkRemovedOptionModule [ "hyperhive" "matrix" "enable" ] matrixEnableRemoved) - (lib.mkRemovedOptionModule [ "services" "hyperhive" "agent" "matrix" "enable" ] matrixEnableRemoved) - # Both spellings, for the reason given above. - (lib.mkRemovedOptionModule [ "hyperhive" "hiveName" ] hiveNameRemoved) + # The hive supplies both at runtime; a definition fails the eval, naming + # where the value comes from. (lib.mkRemovedOptionModule [ "services" "hyperhive" "agent" "hiveName" ] hiveNameRemoved) - (lib.mkRemovedOptionModule [ "hyperhive" "claudeMemoryMaxBytes" ] memoryMaxBytesRemoved) (lib.mkRemovedOptionModule [ "services" "hyperhive" diff --git a/nix/agent-modules/renamed-options.nix b/nix/agent-modules/renamed-options.nix deleted file mode 100644 index 82adcc27..00000000 --- a/nix/agent-modules/renamed-options.nix +++ /dev/null @@ -1,207 +0,0 @@ -# Deprecation shims for the agent tier's top-level `hyperhive.*` spelling. -# -# Per-agent options live at `services.hyperhive.agent.`, beside the host -# tier's `services.hyperhive.*`. These entries keep an `agent.nix` that still -# uses the top-level `hyperhive.` spelling evaluating, with a -# deprecation warning. -# -# ⚠️ One entry per LEAF, not per namespace. A single entry for a parent path -# only covers its children when the parent is a submodule-typed option; every -# nested option here (`user`, `mcp`, `otel`, …) is a plain attrset of -# declarations instead, so each leaf needs its own line — the same reason -# `../host-modules/deploy.nix` renames the CI runner's knobs one by one. -{ lib, ... }: -{ - imports = [ - (lib.mkRenamedOptionModule - [ "hyperhive" "_bashEnvFragments" ] - [ "services" "hyperhive" "agent" "_bashEnvFragments" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "allowedRecipients" ] - [ "services" "hyperhive" "agent" "allowedRecipients" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "autoCompact" ] - [ "services" "hyperhive" "agent" "autoCompact" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "availableModels" ] - [ "services" "hyperhive" "agent" "availableModels" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "backendEnvironmentFile" ] - [ "services" "hyperhive" "agent" "backendEnvironmentFile" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "cargo" "shortMessages" ] - [ "services" "hyperhive" "agent" "cargo" "shortMessages" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "claudeCodePath" ] - [ "services" "hyperhive" "agent" "claudeCodePath" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "claudeMarketplaces" ] - [ "services" "hyperhive" "agent" "claudeMarketplaces" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "claudePlugins" ] - [ "services" "hyperhive" "agent" "claudePlugins" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "claudePluginsAutoUpdate" ] - [ "services" "hyperhive" "agent" "claudePluginsAutoUpdate" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "dashboardLinks" ] - [ "services" "hyperhive" "agent" "dashboardLinks" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "docs" "enable" ] - [ "services" "hyperhive" "agent" "docs" "enable" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "docs" "source" ] - [ "services" "hyperhive" "agent" "docs" "source" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "effortLevel" ] - [ "services" "hyperhive" "agent" "effortLevel" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "extraMcpServers" ] - [ "services" "hyperhive" "agent" "extraMcpServers" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "extraWebProxies" ] - [ "services" "hyperhive" "agent" "extraWebProxies" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "forge" "url" ] - [ "services" "hyperhive" "agent" "forge" "url" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "frontend" "dist" ] - [ "services" "hyperhive" "agent" "frontend" "dist" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "frontend" "extraFiles" ] - [ "services" "hyperhive" "agent" "frontend" "extraFiles" ] - ) - # `frontend.mergedDist` deliberately has NO entry: - # it is `readOnly` and ./frontend.nix already defines it, and a rename - # module contributes a *definition* to its target — so a shim for it makes - # every evaluation fail with "set multiple times". Nothing could have set - # it anyway; a reader of the old path gets "option does not exist", - # which says the same thing sooner. Same reasoning as the `hostUsersFile` - # exclusion in ../host-modules/deploy.nix. - (lib.mkRenamedOptionModule - [ "hyperhive" "github" "enable" ] - [ "services" "hyperhive" "agent" "github" "enable" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "gui" "enable" ] - [ "services" "hyperhive" "agent" "gui" "enable" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "gui" "vncPort" ] - [ "services" "hyperhive" "agent" "gui" "vncPort" ] - ) - (lib.mkRenamedOptionModule [ "hyperhive" "icon" ] [ "services" "hyperhive" "agent" "icon" ]) - (lib.mkRenamedOptionModule - [ "hyperhive" "logs" "queryUrl" ] - [ "services" "hyperhive" "agent" "logs" "queryUrl" ] - ) - # `matrix.enable` is deliberately absent from this list: it has no - # replacement option, and a rename pointing at an undeclared option - # breaks evaluation for everyone. Both of its spellings are - # `mkRemovedOptionModule` in ./default.nix instead. - (lib.mkRenamedOptionModule - [ "hyperhive" "matrix" "url" ] - [ "services" "hyperhive" "agent" "matrix" "url" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "matrixAccounts" ] - [ "services" "hyperhive" "agent" "matrixAccounts" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "mcp" "bashHttpPort" ] - [ "services" "hyperhive" "agent" "mcp" "bashHttpPort" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "mcp" "httpPort" ] - [ "services" "hyperhive" "agent" "mcp" "httpPort" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "mcp" "matrixHttpPort" ] - [ "services" "hyperhive" "agent" "mcp" "matrixHttpPort" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "mcp" "subagentHttpPort" ] - [ "services" "hyperhive" "agent" "mcp" "subagentHttpPort" ] - ) - (lib.mkRenamedOptionModule [ "hyperhive" "model" ] [ "services" "hyperhive" "agent" "model" ]) - (lib.mkRenamedOptionModule - [ "hyperhive" "otel" "debug" ] - [ "services" "hyperhive" "agent" "otel" "debug" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "otel" "enable" ] - [ "services" "hyperhive" "agent" "otel" "enable" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "otel" "endpoint" ] - [ "services" "hyperhive" "agent" "otel" "endpoint" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "otel" "extraResourceAttributes" ] - [ "services" "hyperhive" "agent" "otel" "extraResourceAttributes" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "otel" "metricIntervalMs" ] - [ "services" "hyperhive" "agent" "otel" "metricIntervalMs" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "otel" "protocol" ] - [ "services" "hyperhive" "agent" "otel" "protocol" ] - ) - (lib.mkRenamedOptionModule [ "hyperhive" "packages" ] [ "services" "hyperhive" "agent" "packages" ]) - (lib.mkRenamedOptionModule - [ "hyperhive" "queue" "natsUrl" ] - [ "services" "hyperhive" "agent" "queue" "natsUrl" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "queue" "tokenEndpoint" ] - [ "services" "hyperhive" "agent" "queue" "tokenEndpoint" ] - ) - # `queue.clientIdFile` / `queue.clientSecretFile` are excluded for the - # `mergedDist` reason above: both are `readOnly`, so a shim's definition - # collides with the option's own and breaks every evaluation. They are - # derived paths no agent config could have set. - (lib.mkRenamedOptionModule - [ "hyperhive" "swarmName" ] - [ "services" "hyperhive" "agent" "swarmName" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "useApiKey" ] - [ "services" "hyperhive" "agent" "useApiKey" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "user" "gid" ] - [ "services" "hyperhive" "agent" "user" "gid" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "user" "name" ] - [ "services" "hyperhive" "agent" "user" "name" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "user" "passwordlessSudo" ] - [ "services" "hyperhive" "agent" "user" "passwordlessSudo" ] - ) - (lib.mkRenamedOptionModule - [ "hyperhive" "user" "uid" ] - [ "services" "hyperhive" "agent" "user" "uid" ] - ) - ]; -} diff --git a/nix/docs/default.nix b/nix/docs/default.nix index 47f15315..c8c0c028 100644 --- a/nix/docs/default.nix +++ b/nix/docs/default.nix @@ -212,9 +212,9 @@ let grafana, the swarm controller, authelia, …). - [per-agent options](agent.md) — options declared in `nix/agent-modules/`, visible from every `agent.nix` - (`hyperhive.model`, `hyperhive.allowedRecipients`, - `hyperhive.extraMcpServers`, `hyperhive.frontend.*`, - `hyperhive.forge.*`, `hyperhive.matrix.*`, `hyperhive.gui.*`). + (`services.hyperhive.agent.model`, `services.hyperhive.agent.allowedRecipients`, + `services.hyperhive.agent.extraMcpServers`, `services.hyperhive.agent.frontend.*`, + `services.hyperhive.agent.forge.*`, `services.hyperhive.agent.matrix.*`, `services.hyperhive.agent.gui.*`). Regenerate with `nix build .#docs` (bundle), `.#docs-host`, `.#docs-swarm`, `.#docs-deploy`, or `.#docs-agent`. Consumed by the diff --git a/nix/host-modules/default.nix b/nix/host-modules/default.nix index b16f74a1..24154099 100644 --- a/nix/host-modules/default.nix +++ b/nix/host-modules/default.nix @@ -87,7 +87,6 @@ ./swarm.nix ./host-ca-removed.nix ./nats-callout-removed.nix - ./swarm-peers-removed.nix ./swarm-required-services.nix ]; } diff --git a/nix/host-modules/deploy.nix b/nix/host-modules/deploy.nix index 80dcc827..f8789d4c 100644 --- a/nix/host-modules/deploy.nix +++ b/nix/host-modules/deploy.nix @@ -26,8 +26,7 @@ # # ⚠️ The renames below are deliberately in this one file rather than # spread across the service modules, so all of them have a single home -# and a single file to delete when the deprecation window closes — the -# shape ./swarm-peers-removed.nix already uses. +# and a single file to delete when the deprecation window closes. { config, lib, ... }: let # The swarm UI's own switch, in both spellings. Accepted and ignored: a @@ -46,7 +45,7 @@ let lib.optional (value != null) "services.hyperhive.${path} no longer has any effect: swarm-ui now runs wherever swarm-controller runs. Remove this setting; services.hyperhive.deploy.swarm-controller.enable decides it."; - # Shared by the two removed spellings of the status-publishing secret below. + # The removed status-publishing secret below. statusSecretRemoved = '' hive-c0re reads this hive's status-publishing client secret from the swarm's secret store, under the hive's own certificate @@ -57,101 +56,11 @@ let in { imports = [ - # Same type, same meaning, new path — so a rename carries it exactly - # and existing configs keep evaluating with one warning naming both - # paths. Precedent: ./hive-forge/service.nix, ./hive-matrix.nix. - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "grafana" "enable" ] - [ "services" "hyperhive" "deploy" "grafana" "enable" ] - ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "grafana" "datasourceUrl" ] - [ "services" "hyperhive" "deploy" "grafana" "datasourceUrl" ] - ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "grafana" "logsDatasourceUrl" ] - [ "services" "hyperhive" "deploy" "grafana" "logsDatasourceUrl" ] - ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "grafana" "plugins" ] - [ "services" "hyperhive" "deploy" "grafana" "plugins" ] - ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "victoriametrics" "enable" ] - [ "services" "hyperhive" "deploy" "victoriametrics" "enable" ] - ) (lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "victoriametrics" "port" ] '' VictoriaMetrics listens on a unix socket in a network namespace of its own, and the gateway reaches it there, so it binds no host port. Remove this definition. '') - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "victorialogs" "enable" ] - [ "services" "hyperhive" "deploy" "victorialogs" "enable" ] - ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "controller" "enable" ] - [ "services" "hyperhive" "deploy" "swarm-controller" "enable" ] - ) - (lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "controller" "forgeTokenFile" ] '' - The controller reads its forge token from the swarm's secret store, under - its own certificate, and forgejo-swarm-controller-account in the forge's - container puts it there. Remove this definition; the file it named is - read by nothing now and can be deleted. - '') - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "controller" "authBridgeUrl" ] - [ "services" "hyperhive" "deploy" "swarm-controller" "authBridgeUrl" ] - ) - (lib.mkRemovedOptionModule - [ "services" "hyperhive" "swarm" "controller" "queue" "clientSecretFile" ] - '' - The controller reads its queue client secret from the swarm's secret - store, under its own certificate (deploy.swarm-controller.baoClientCertFile), - and swarm-secret-publish on the host that runs authelia puts it there. - Remove this definition; the file it named is read by nothing now and can - be deleted. - '' - ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "authelia" "enable" ] - [ "services" "hyperhive" "deploy" "authelia" "enable" ] - ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "authelia" "usersFile" ] - [ "services" "hyperhive" "deploy" "authelia" "usersFile" ] - ) - # `hostClientSecretDir`, `hostUsersFile` and `grafana.socketDir` - # deliberately have NO entry here. A rename module contributes a - # *definition* to its target, and all three are `readOnly`, which refuses a - # second one — so a shim for any of them makes every evaluation fail with - # "set multiple times", naming the option's own default as the rival - # definition. - # - # The first two are derived, so nothing could ever have set them and a - # shim's only customer is a reader of the old path. `grafana.socketDir` was - # a plain settable option; only a test fixture set it in - # this tree, but a deployment outside it can have. That setter now fails - # evaluation with "option does not exist" at the old path, rather than - # being carried to the new one. - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "nats" "enable" ] - [ "services" "hyperhive" "deploy" "nats" "enable" ] - ) - # `tokenEndpoint` stays under `swarm.*`: it is the swarm IdP's one - # address, identical on every hive, where this is "the queue as seen from - # here". - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "statusPublish" "natsUrl" ] - [ "services" "hyperhive" "deploy" "hive-controller" "statusPublish" "natsUrl" ] - ) - (lib.mkRemovedOptionModule [ - "services" - "hyperhive" - "swarm" - "statusPublish" - "clientSecretFile" - ] statusSecretRemoved) (lib.mkRemovedOptionModule [ "services" "hyperhive" @@ -160,27 +69,11 @@ in "statusPublish" "clientSecretFile" ] statusSecretRemoved) - # Both old spellings of "run a hive here" forward to the one switch. - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "enable" ] - [ "services" "hyperhive" "deploy" "hive-controller" "enable" ] - ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "c0re" "enable" ] - [ "services" "hyperhive" "deploy" "hive-controller" "enable" ] - ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "otel" "enable" ] - [ "services" "hyperhive" "deploy" "swarm-otel" "enable" ] - ) - # ⚠️ Not to be confused with `services.hyperhive.otel.clientSecretFile`, - # a different option on the hive tier that is not part of this table. A - # bare `clientSecretFile` matches five distinct options across the tree. - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "otel" "clientSecretFile" ] - [ "services" "hyperhive" "deploy" "swarm-otel" "clientSecretFile" ] - ) + # Same type, same meaning, new path — so a rename carries it exactly + # and existing configs keep evaluating with one warning naming both + # paths. + # # The runner moved from `deploy.forgejo.ci` to `deploy.ci-runner` with # every option keeping its type and meaning, so the old paths forward. (lib.mkRenamedOptionModule @@ -219,69 +112,6 @@ in [ "services" "hyperhive" "deploy" "bao" "servicesIssuerClientKeyFile" ] ) - # The runner toggle sat under `swarm.forge.ci` before `deploy.forgejo.ci`. - # Both were per-host, so each forwards straight to its final path. - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "forge" "ci" "enable" ] - [ "services" "hyperhive" "deploy" "ci-runner" "enable" ] - ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "forge" "ci" "name" ] - [ "services" "hyperhive" "deploy" "ci-runner" "name" ] - ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "forge" "ci" "concurrency" ] - [ "services" "hyperhive" "deploy" "ci-runner" "concurrency" ] - ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "forge" "ci" "labels" ] - [ "services" "hyperhive" "deploy" "ci-runner" "labels" ] - ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "forge" "ci" "package" ] - [ "services" "hyperhive" "deploy" "ci-runner" "package" ] - ) - - # The rest of the forge split. What stays under `swarm.forge` is what the - # forge IS from any hive's point of view — the names and ports it answers - # on, the URLs it advertises, the client id it is registered under; the - # two renamed here are what the host running it decides. Its package is - # renamed further down with the other `*.package` entries. The two removed - # here named host files that the swarm secret store now replaces. - # - # `mirrors` renames in one entry rather than one per field: it is a single - # option of a list-of-submodule type, so the rename carries its whole - # value. - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "forge" "openFirewall" ] - [ "services" "hyperhive" "deploy" "forgejo" "openFirewall" ] - ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "forge" "mirrors" ] - [ "services" "hyperhive" "deploy" "forgejo" "mirrors" ] - ) - (lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "forge" "sso" "clientSecretFile" ] '' - The forge's OIDC client secret is fetched from the swarm secret store - (swarm-bao-forge-oidc) into a fixed runtime path. Remove this - definition. - '') - (lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "forge" "hostSwarmControllerTokenFile" ] - '' - The forge publishes the swarm-controller's forge token to the swarm - secret store (forgejo-swarm-controller-account), and the controller - reads it there. Remove this definition; the file it named is read by - nothing now and can be deleted. - '' - ) - - # The homeserver, split the same way. `swarm.matrix` keeps what it IS to - # every hive — the name it answers to, the URLs it is reached on, the - # client id it is registered under. These six are what the host - # running it decides; its two packages are renamed further down with - # the other `*.package` entries. ⚠️ `enable` is NOT here: its shim lives in - # ./hive-matrix.nix, so that file is the one - # to check before assuming this table is the whole matrix story. - # # The homeserver's port and its firewall switch, removed rather than # renamed: tuwunel listens on a unix socket only the gateway reaches, so # there is no host port to choose or open. @@ -291,111 +121,20 @@ in Remove this definition. Clients and federation peers reach it at services.hyperhive.swarm.matrix.gatewayHost. '') - (lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "matrix" "openFirewall" ] '' - The homeserver no longer listens on a host port, so there is no port - to open. Remove this definition. Reach from outside the host goes - through the gateway: services.hyperhive.gateway.openFirewall. - '') (lib.mkRemovedOptionModule [ "services" "hyperhive" "deploy" "matrix" "openFirewall" ] '' The homeserver no longer listens on a host port, so there is no port to open. Remove this definition. Reach from outside the host goes through the gateway: services.hyperhive.gateway.openFirewall. '') - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "matrix" "trustedServers" ] - [ "services" "hyperhive" "deploy" "matrix" "trustedServers" ] - ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "matrix" "maxRequestSize" ] - [ "services" "hyperhive" "deploy" "matrix" "maxRequestSize" ] - ) - # Both spellings of the registration token, removed rather than renamed: - # the homeserver does not accept a shared registration secret at all, - # so there is no path to point a definition at. Accounts are created by - # the swarm's appservice. - (lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "matrix" "registrationTokenFile" ] '' - The matrix registration token is gone: swarm-controller creates accounts - as the swarm's appservice instead, so there is no shared secret for - tuwunel to check. Remove this definition — nothing needs replacing. - The old token file at /var/lib/hyperhive/matrix-register-token is read by - nothing now and can be deleted. See docs/integrations/matrix.md. - '') - (lib.mkRemovedOptionModule [ "services" "hyperhive" "deploy" "matrix" "registrationTokenFile" ] '' - The matrix registration token is gone: swarm-controller creates accounts - as the swarm's appservice instead, so there is no shared secret for - tuwunel to check. Remove this definition — nothing needs replacing. - The old token file at /var/lib/hyperhive/matrix-register-token is read by - nothing now and can be deleted. See docs/integrations/matrix.md. - '') - (lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "matrix" "gui" "enable" ] '' - The matrix web client has no on/off switch: every host that runs the - homeserver serves it at services.hyperhive.swarm.matrix.gatewayHost, - and the swarm UI links to it. Remove this definition. - '') (lib.mkRemovedOptionModule [ "services" "hyperhive" "deploy" "matrix" "gui" "enable" ] '' The matrix web client has no on/off switch: every host that runs the homeserver serves it at services.hyperhive.swarm.matrix.gatewayHost, and the swarm UI links to it. Remove this definition. '') - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "matrix" "sso" "clientSecretFile" ] - [ "services" "hyperhive" "deploy" "matrix" "sso" "clientSecretFile" ] - ) - # Retention is read only where the container is defined, so it is a - # decision of the host running the store rather than something the swarm - # agrees on. The two stores keep their domain and port in `swarm.*`, - # because a client hive needs those to reach them; `package` followed - # `enable` and `retentionPeriod` across in a later slice. - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "victoriametrics" "retentionPeriod" ] - [ "services" "hyperhive" "deploy" "victoriametrics" "retentionPeriod" ] - ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "victorialogs" "retentionPeriod" ] - [ "services" "hyperhive" "deploy" "victorialogs" "retentionPeriod" ] - ) - - # The switch over all of the above. `allSwarmServices` says whether THIS - # host runs every service the swarm requires; those services are - # required of the SWARM, so the name is about the swarm rather than this - # host's wiring. - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "enableRequiredServices" ] - [ "services" "hyperhive" "deploy" "allSwarmServices" ] - ) - - # The mode above that one lives under `deploy`, not at the TOP of - # `services.hyperhive`: that namespace is everything about hyperhive, not - # the settings of a single hive. `singleHostSwarm` names what the mode - # asserts — the whole swarm runs on this host — instead of its mechanism. - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "enableAllLocalDefaults" ] - [ "services" "hyperhive" "deploy" "singleHostSwarm" ] - ) - - # The hive TLS state dir lives under `deploy.hive-controller` (the - # hive-c0re daemon's namespace), not at the TOP of `services.hyperhive`, - # which is everything about hyperhive rather than the settings of one hive. - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "tls" "stateDir" ] - [ "services" "hyperhive" "deploy" "hive-controller" "tls" "stateDir" ] - ) - # The hive CA's lifetime and its leaves', both spellings, removed rather - # than renamed: no hive CA is generated, and the store's per-hive role - # sets the gateway leaf's lifetime. - (lib.mkRemovedOptionModule [ "services" "hyperhive" "tls" "caValidityDays" ] '' - No hive CA is generated any more: the gateway leaf is issued by the - secret store's pki mount, whose per-hive role sets its lifetime - (services.hyperhive.deploy.bao.servicesPkiLeafTtlHours). Remove this - definition. See docs/swarm/ca.md. - '') - (lib.mkRemovedOptionModule [ "services" "hyperhive" "tls" "leafValidityDays" ] '' - No hive CA is generated any more: the gateway leaf is issued by the - secret store's pki mount, whose per-hive role sets its lifetime - (services.hyperhive.deploy.bao.servicesPkiLeafTtlHours). Remove this - definition. See docs/swarm/ca.md. - '') + # The hive CA's lifetime and its leaves', removed rather than renamed: no + # hive CA is generated, and the store's per-hive role sets the gateway + # leaf's lifetime. (lib.mkRemovedOptionModule [ "services" "hyperhive" "deploy" "hive-controller" "tls" "caValidityDays" ] '' @@ -415,98 +154,12 @@ in '' ) - # The WireGuard mesh, whole. Unlike every rename above this one covers a - # namespace rather than a toggle: nothing under it is a fact another hive - # reads. A peer needs this host's `wireguardEndpoint` — the roster entry - # in ./swarm.nix, which stays — and nothing about the interface this host - # brings up. `listenPort` follows for the same reason and is easy to read - # the other way: it is what this host *binds*, while the port a peer - # *dials* is the one inside `wireguardEndpoint`. - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "wireguard" "enable" ] - [ "services" "hyperhive" "deploy" "wireguard" "enable" ] - ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "wireguard" "privateKeyFile" ] - [ "services" "hyperhive" "deploy" "wireguard" "privateKeyFile" ] - ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "wireguard" "address" ] - [ "services" "hyperhive" "deploy" "wireguard" "address" ] - ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "wireguard" "listenPort" ] - [ "services" "hyperhive" "deploy" "wireguard" "listenPort" ] - ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "wireguard" "persistentKeepalive" ] - [ "services" "hyperhive" "deploy" "wireguard" "persistentKeepalive" ] - ) - - # Which build a service runs is a decision of the host that runs it, so - # every `*.package` follows its `enable` across. Left in separate - # namespaces they split one client's two halves, as `gui.enable` and - # `gui.package` would. - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "matrix" "package" ] - [ "services" "hyperhive" "deploy" "matrix" "package" ] - ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "matrix" "gui" "package" ] - [ "services" "hyperhive" "deploy" "matrix" "gui" "package" ] - ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "grafana" "package" ] - [ "services" "hyperhive" "deploy" "grafana" "package" ] - ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "victorialogs" "package" ] - [ "services" "hyperhive" "deploy" "victorialogs" "package" ] - ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "victoriametrics" "package" ] - [ "services" "hyperhive" "deploy" "victoriametrics" "package" ] - ) - # With these two `services.hyperhive.swarm.ui` holds nothing at all: its - # `enable` is an ignored switch (declared below) and its `package` is - # renamed here. `deploy.swarm-ui` keeps only its ignored `enable`. - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "ui" "package" ] - [ "services" "hyperhive" "deploy" "swarm-controller" "ui" "package" ] - ) + # `deploy.swarm-ui` keeps only its ignored `enable` (declared below); its + # `package` is the controller's UI package. (lib.mkRenamedOptionModule [ "services" "hyperhive" "deploy" "swarm-ui" "package" ] [ "services" "hyperhive" "deploy" "swarm-controller" "ui" "package" ] ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "nats" "authPackage" ] - [ "services" "hyperhive" "deploy" "nats" "authPackage" ] - ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "authelia" "package" ] - [ "services" "hyperhive" "deploy" "authelia" "package" ] - ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "authelia" "bridgePackage" ] - [ "services" "hyperhive" "deploy" "authelia" "bridgePackage" ] - ) - # ⚠️ `swarm-controller`, not `hive-controller` — see the note in - # ./swarm-controller.nix. Both namespaces exist, so the wrong target here - # lands on a live option in hive-c0re's tree instead of failing. - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "controller" "package" ] - [ "services" "hyperhive" "deploy" "swarm-controller" "package" ] - ) - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "controller" "swarmctlPackage" ] - [ "services" "hyperhive" "deploy" "swarm-controller" "swarmctlPackage" ] - ) - # ⚠️ `deploy.forgejo`, not `deploy.forge` — the deploy-side name differs - # from the swarm-side one here, as it does for the controller and the UI. - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "forge" "package" ] - [ "services" "hyperhive" "deploy" "forgejo" "package" ] - ) ]; # ⚠️ Only `deploy.forgejo.enable` is declared here. The rest of diff --git a/nix/host-modules/hive-c0re/environment.nix b/nix/host-modules/hive-c0re/environment.nix index 818afe55..b114a898 100644 --- a/nix/host-modules/hive-c0re/environment.nix +++ b/nix/host-modules/hive-c0re/environment.nix @@ -67,13 +67,13 @@ in } // lib.optionalAttrs (!config.services.hyperhive.github.enable) { # GitHub integration is on by default; only signal the OFF override to - # meta.rs, which then injects `hyperhive.github.enable = false` into + # meta.rs, which then injects `services.hyperhive.agent.github.enable = false` into # every agent. See services.hyperhive.github.enable. HYPERHIVE_GITHUB_DISABLED = "1"; } // lib.optionalAttrs config.services.hyperhive.otel.enable ( # Hive-wide OTEL config -> read by meta.rs::otel_config and - # injected as build-time `hyperhive.otel.*` into every agent. + # injected as build-time `services.hyperhive.agent.otel.*` into every agent. # Endpoint presence is the enable signal on the meta side; the # optional fields are only emitted when set so absent values # don't render no-op env lines. @@ -172,7 +172,7 @@ in # fallback reads as harmless because hive-c0re shares the host netns — # but the value is handed to *agents*, which do not, so `127.0.0.1` # there names the agent itself. An absent forward leaves - # `hyperhive.matrix.url` null and the daemon no-ops; that is the honest + # `services.hyperhive.agent.matrix.url` null and the daemon no-ops; that is the honest # answer when there is no matrix vhost to point at. HIVE_MATRIX_URL = "http://${config.services.hyperhive.swarm.matrix.gatewayHost}"; } diff --git a/nix/host-modules/hive-c0re/options.nix b/nix/host-modules/hive-c0re/options.nix index ef2f41dc..75ba77e6 100644 --- a/nix/host-modules/hive-c0re/options.nix +++ b/nix/host-modules/hive-c0re/options.nix @@ -76,7 +76,7 @@ description = '' URL of the narrow `docs/` source (no fragment). Inlined into the generated meta `flake.nix` at `inputs.hyperhive-docs.url` and - threaded to each agent as `hyperhive.docs.source`, from which the + threaded to each agent as `services.hyperhive.agent.docs.source`, from which the harness resolves `$HIVE_DOCS_DIR`. Its own store path — separate from `hyperhiveFlake` — so a doc edit only re-locks this input instead of rebuilding every agent container. diff --git a/nix/host-modules/hive-forge/service.nix b/nix/host-modules/hive-forge/service.nix index 18f4e60e..202d4718 100644 --- a/nix/host-modules/hive-forge/service.nix +++ b/nix/host-modules/hive-forge/service.nix @@ -30,27 +30,6 @@ let effectiveRootUrl = if cfg.rootUrl != null then cfg.rootUrl else defaultRootUrl; in { - # Forge lives under `swarm`, beside the other swarm-global services. One - # rename for the top-level `services.hyperhive.forge` namespace: the - # subtree comes with it, so existing hives keep evaluating and get one - # warning naming both paths. - imports = [ - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "forge" ] - [ "services" "hyperhive" "swarm" "forge" ] - ) - (lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "forge" "sso" "enable" ] '' - SSO is no longer optional: the forge always registers the swarm's - authelia as a login source. - - Removed rather than defaulted to true so a config that turned it - OFF fails here, where the line is, instead of silently gaining a - login provider on the next rebuild. Drop the line; if it was - false, set services.hyperhive.swarm.authelia.url as the assertion - describes. - '') - ]; - # Every swarm needs this forge — it's the canonical store for the meta # flake + every agent's config repo (and the `internal/*` repos) — but # only one host runs it: `deploy.forgejo.enable`, in ../deploy.nix. What diff --git a/nix/host-modules/hive-gateway/options.nix b/nix/host-modules/hive-gateway/options.nix index ecf9994c..beb3a5e9 100644 --- a/nix/host-modules/hive-gateway/options.nix +++ b/nix/host-modules/hive-gateway/options.nix @@ -12,12 +12,6 @@ let in { imports = [ - (lib.mkRemovedOptionModule [ "services" "hyperhive" "gateway" "selfSignedTls" ] '' - Self-signed TLS is the implicit default whenever neither - tls.certDir nor tls.acme is configured, and there is no http-only - mode. Remove the setting; configure `tls.certDir` or `tls.acme` - to override the self-signed default. - '') (lib.mkRemovedOptionModule [ "services" "hyperhive" "gateway" "auth" ] '' The dashboard is served on services.hyperhive.swarm.dashboardDomain behind authelia, and only accounts in authelia's `admins` group can diff --git a/nix/host-modules/hive-matrix-service.nix b/nix/host-modules/hive-matrix-service.nix index 6e9e0cfc..52301cb6 100644 --- a/nix/host-modules/hive-matrix-service.nix +++ b/nix/host-modules/hive-matrix-service.nix @@ -61,7 +61,7 @@ in Client-server API base URL **hive-c0re itself** uses to provision matrix (register agent users, create the hive space and chat room, invite members). Distinct from the agent-facing - `hyperhive.matrix.url`, which is the gateway vhost handed to + `services.hyperhive.agent.matrix.url`, which is the gateway vhost handed to each agent's `hive-matrix-daemon`. Defaults to `https://` on the homeserver's gateway vhost diff --git a/nix/host-modules/hive-matrix.nix b/nix/host-modules/hive-matrix.nix index 7b008c57..2fcbe0ff 100644 --- a/nix/host-modules/hive-matrix.nix +++ b/nix/host-modules/hive-matrix.nix @@ -281,42 +281,12 @@ in # settings: docs/integrations/matrix.md. Vhost map + discovery flow + tuning # knobs: docs/networking/gateway.md. - # Matrix lives under `swarm`, beside the other swarm-global services. One - # rename for the top-level `services.hyperhive.matrix` namespace: the - # subtree comes with it, so existing hives keep evaluating and get one - # warning naming both paths. - imports = [ - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "matrix" ] - [ "services" "hyperhive" "swarm" "matrix" ] - ) - # "does THIS host run it" is a per-host decision, and `swarm.*` is the - # namespace every host in the swarm agrees on. What stays under - # `swarm.matrix` is the homeserver's identity as every hive sees it — - # its server name, its addresses, its federation policy. - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarm" "matrix" "enable" ] - [ "services" "hyperhive" "deploy" "matrix" "enable" ] - ) - (lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "matrix" "sso" "enable" ] '' - SSO is no longer optional: a homeserver that runs at all delegates - login to the swarm's authelia. - - Removed rather than defaulted to true so a config that turned it - OFF fails here, where the line is, instead of silently gaining a - login flow on the next rebuild. Drop the line; if it was false, - set services.hyperhive.deploy.matrix.sso.clientSecretFile and - services.hyperhive.swarm.authelia.url as the assertions describe. - '') - ]; - # `swarm.matrix`, in ./hive-matrix-service.nix, is what the homeserver IS from # any hive's point of view: the name it answers to, the URLs it is # reached on, and the client id it is registered under. What lives here is what the host running it # decides — which build it runs, whether it is exposed, which peers it trusts, # how large a request it accepts, and where its host-local secrets sit. Same rule as - # ./swarm-victorialogs.nix; `enable` already lives in ./deploy.nix, which also - # carries the renames. + # ./swarm-victorialogs.nix; `enable` lives in ./deploy.nix. options.services.hyperhive.deploy.matrix = { package = lib.mkOption { type = lib.types.package; diff --git a/nix/host-modules/hive-network.nix b/nix/host-modules/hive-network.nix index 1cc02e37..52c5659e 100644 --- a/nix/host-modules/hive-network.nix +++ b/nix/host-modules/hive-network.nix @@ -63,19 +63,6 @@ in # Hive-internal network — the host-side bridge every container in a # private netns hangs off. Full design: docs/networking/network.md. - imports = [ - (lib.mkRemovedOptionModule [ "services" "hyperhive" "network" "isolateContainers" ] '' - Network isolation is the only mode and is always on whenever - the hive runs; the shared-netns path was removed. Remove - the setting. - '') - (lib.mkRemovedOptionModule [ "services" "hyperhive" "network" "upstreamDns" ] '' - The hive resolver always follows the host's resolvers now - (dnsmasq runs on the host and reads its /etc/resolv.conf - directly). Configure upstream DNS on the host itself instead. - '') - ]; - options.services.hyperhive.network = { enable = lib.mkOption { type = lib.types.bool; diff --git a/nix/host-modules/host-ca-removed.nix b/nix/host-modules/host-ca-removed.nix index 50fcc97f..27c9e2c9 100644 --- a/nix/host-modules/host-ca-removed.nix +++ b/nix/host-modules/host-ca-removed.nix @@ -9,7 +9,7 @@ # a removed option fails evaluation with a message, a CA-file option naming # one of the deleted paths fails it too, and every host deletes the files. # -# Self-contained and deletable, like ./swarm-peers-removed.nix: one file to +# Self-contained and deletable: one file to # remove when the deprecation window closes. { lib, config, ... }: let diff --git a/nix/host-modules/hyperhive.nix b/nix/host-modules/hyperhive.nix index 8741446a..23083851 100644 --- a/nix/host-modules/hyperhive.nix +++ b/nix/host-modules/hyperhive.nix @@ -11,14 +11,7 @@ let hiveCfg = config.services.hyperhive; in { - # The swarm's display name lives under `swarm`, with the other - # swarm-global settings; the hive's own name and domain are top-level, - # because they describe this hive rather than the swarm it joins. imports = [ - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "swarmName" ] - [ "services" "hyperhive" "swarm" "name" ] - ) (lib.mkRemovedOptionModule [ "services" "hyperhive" "ruthless" ] '' hive-c0re no longer creates ruth at boot. Remove this definition, and create ruth from the swarm dashboard like any other agent; a ruth container that @@ -205,11 +198,11 @@ in example = false; description = '' Hive-wide switch for the per-agent GitHub integration (the `gh` CLI - wrapper + git credential helper, per `hyperhive.github.enable`). On by + wrapper + git credential helper, per `services.hyperhive.agent.github.enable`). On by default: every agent gets the integration, inert until an operator links a PAT for it in the swarm UI. Set `false` to turn it off for the whole hive --- the meta-flake renderer (`hive-c0re/src/meta.rs`) then injects - `hyperhive.github.enable = false` into every agent. Exposed to hive-c0re + `services.hyperhive.agent.github.enable = false` into every agent. Exposed to hive-c0re as `HYPERHIVE_GITHUB_DISABLED` (set only when the integration is off). ''; }; diff --git a/nix/host-modules/otel.nix b/nix/host-modules/otel.nix index 49699439..92e5219b 100644 --- a/nix/host-modules/otel.nix +++ b/nix/host-modules/otel.nix @@ -2,7 +2,7 @@ # Set ONCE here at host level; the meta-flake renderer # (`hive-c0re/src/meta.rs::otel_config`) reads the HYPERHIVE_OTEL_* env # exported off hive-c0re's unit (see ./hive-c0re) and injects the matching -# `hyperhive.otel.*` build-time config into EVERY agent (mirroring the +# `services.hyperhive.agent.otel.*` build-time config into EVERY agent (mirroring the # CA-cert injection), so each agent's harness exports its own Claude Code # stats directly to the collector. There is no per-agent opt-in — this is # the single switch for the whole hive. diff --git a/nix/host-modules/swarm-bao.nix b/nix/host-modules/swarm-bao.nix index 070ec974..15b11320 100644 --- a/nix/host-modules/swarm-bao.nix +++ b/nix/host-modules/swarm-bao.nix @@ -1530,14 +1530,7 @@ let storeRefreshTimer = import ./lib/store-refresh-timer.nix { }; in { - imports = [ - (lib.mkRemovedOptionModule [ "services" "hyperhive" "deploy" "bao" "matrixCtlHiveName" ] '' - swarm-controller is the only minter of the per-hive matrix sender token, - and this option named a write grant matrix-ctl no longer holds. Delete - this setting. - '') - ] - ++ + imports = map ( option: diff --git a/nix/host-modules/swarm-controller.nix b/nix/host-modules/swarm-controller.nix index a9e4b1c5..99c099f9 100644 --- a/nix/host-modules/swarm-controller.nix +++ b/nix/host-modules/swarm-controller.nix @@ -285,22 +285,6 @@ in hostUnit = true; enable = deployCfg.swarm-controller.enable; }) - # The avatar belongs to the forge-objects pass that uploads it, which - # runs in this daemon. - (lib.mkRenamedOptionModule - [ "services" "hyperhive" "c0re" "orgAvatarPng" ] - [ "services" "hyperhive" "deploy" "swarm-controller" "configOrgAvatarPng" ] - ) - (lib.mkRemovedOptionModule - [ "services" "hyperhive" "deploy" "swarm-controller" "queue" "clientSecretFile" ] - '' - The controller reads its queue client secret from the swarm's secret - store, under its own certificate (deploy.swarm-controller.baoClientCertFile), - and swarm-secret-publish on the host that runs authelia puts it there. - Remove this definition; the file it named is read by nothing now and can - be deleted. - '' - ) (lib.mkRemovedOptionModule [ "services" "hyperhive" "deploy" "swarm-controller" "forgeTokenFile" ] '' The controller reads its forge token from the swarm's secret store, under @@ -421,7 +405,7 @@ in description = '' Quick links to swarm-wide services, surfaced by the swarm UI's links menu (`GET /api/links`). Same shape and same - zero-code-change-to-extend idea as `hyperhive.dashboardLinks` + zero-code-change-to-extend idea as `services.hyperhive.agent.dashboardLinks` (`nix/agent-modules/dashboard-links.nix`), one level up: rather than one fixed list, definitions merge with the list-merge idiom `services.hyperhive.gateway.localNames` uses, so an operator can diff --git a/nix/host-modules/swarm-otel-service.nix b/nix/host-modules/swarm-otel-service.nix index 2ed0362e..13c74ab1 100644 --- a/nix/host-modules/swarm-otel-service.nix +++ b/nix/host-modules/swarm-otel-service.nix @@ -33,15 +33,6 @@ let }; in { - imports = [ - (lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "otel" "journaldUnits" ] '' - The swarm collector no longer takes a unit allowlist: it ships the - journal of every system unit on the host and drops only user - sessions (records under a `user-.slice`). Delete the setting; - nothing replaces it. - '') - ]; - # `enable` is `services.hyperhive.deploy.swarm-otel.enable` — see ./deploy.nix. # ⚠️ That is the SWARM collector. The per-hive one keeps its own # `services.hyperhive.otel.enable` (./otel.nix) and is a different diff --git a/nix/host-modules/swarm-peers-removed.nix b/nix/host-modules/swarm-peers-removed.nix deleted file mode 100644 index 4e3a1560..00000000 --- a/nix/host-modules/swarm-peers-removed.nix +++ /dev/null @@ -1,67 +0,0 @@ -# Migration shim for `services.hyperhive.swarm.peers`, replaced by the -# `swarm.hives` directory in ./swarm.nix. -# -# ⚠️ This is not a `mkRenamedOptionModule`. `hives` is not `peers` -# under another name, it is **`peers` ∪ {self}**: the set gains a member no -# existing config has ever written down, because a host's own identity -# lives in *other* options entirely. A rename migrates a name and a -# default can re-root a meaning; neither can conjure a new member. -# -# The whole module is self-contained and deletable — one file to remove -# when the deprecation window closes, with nothing else referring to it. -# -# Deliberately a warning rather than a hard failure, with the loudness -# coming from elsewhere: a config that set only `peers` leaves `hives` -# empty, so the self-entry assertion in ./swarm.nix fails the build -# anyway. The warning is what explains it; the assertion is what stops -# it. -{ - lib, - config, - ... -}: -let - peers = config.services.hyperhive.swarm.peers; - withCaCert = lib.attrNames (lib.filterAttrs (_: p: p ? caCert && p.caCert != null) peers); -in -{ - options.services.hyperhive.swarm.peers = lib.mkOption { - type = lib.types.attrsOf lib.types.anything; - default = { }; - visible = false; - internal = true; - description = '' - Removed — use `services.hyperhive.swarm.hives` instead, which - describes every hive in the swarm including this one. Kept only so - an existing definition produces a warning that says where to move - it, rather than an "option does not exist" error that says nothing. - ''; - }; - - config.warnings = - lib.optional (peers != { }) '' - services.hyperhive.swarm.peers is removed and ignored. Move these - entries to services.hyperhive.swarm.hives, keyed by hive NAME - rather than domain, and add an entry for this hive itself - (services.hyperhive.hiveName) — `hives` is the swarm's full - directory, identical on every host. - - services.hyperhive.swarm.hives. = { - domain = ""; - # wireguard* carries over unchanged - }; - - Still set: ${lib.concatStringsSep ", " (lib.attrNames peers)} - '' - ++ lib.optional (withCaCert != [ ]) '' - services.hyperhive.swarm.peers..caCert is removed and - ignored, and has no replacement in services.hyperhive.swarm.hives. - - Trust inside a swarm derives from the secret store's pki root (see - docs/swarm/ca.md): every hive chains to it, so a per-hive CA is dead - weight. What this genuinely drops is trusting a hive whose root this - swarm does NOT own — another swarm's, or one keeping its own CA. - - Still set on: ${lib.concatStringsSep ", " withCaCert} - ''; -} diff --git a/nix/host-modules/swarm-ui.nix b/nix/host-modules/swarm-ui.nix index 35566bb0..2e25b2b9 100644 --- a/nix/host-modules/swarm-ui.nix +++ b/nix/host-modules/swarm-ui.nix @@ -70,18 +70,6 @@ in # `services.hyperhive.deploy.swarm-controller.enable` is, because it reaches # that daemon over its unix socket. There is no `domain` option: the UI # answers on the swarm apex, which the swarm already declares once. - imports = [ - (lib.mkRemovedOptionModule [ "services" "hyperhive" "swarm" "ui" "domain" ] '' - The swarm UI answers on services.hyperhive.swarm.domain and nothing - else. It shares that name with the swarm-controller it fronts — they - are one service to a reader and to a certificate — so a second option - only ever created two spellings of one address. - - Removed rather than aliased: a config still setting it was pinning a - name the controller's own public URL never used, and silently keeping - the alias would preserve exactly that mismatch. - '') - ]; # ⚠️ Neither `services.hyperhive.swarm.ui` nor `deploy.swarm-ui` is a # namespace for anything but the old spellings: the UI's `package` lives diff --git a/nix/host-modules/swarm.nix b/nix/host-modules/swarm.nix index d80b0d55..81f2e24d 100644 --- a/nix/host-modules/swarm.nix +++ b/nix/host-modules/swarm.nix @@ -118,13 +118,6 @@ let # never gets a gateway certificate. swarmCfg.bao.ui.domain ]; - - # Hives whose entry still carries the removed `certFingerprint`. Scanned - # here, at top level, because that is the only place an assertion about a - # submodule field can actually live — see the option's own comment. - pinnedHives = lib.attrNames ( - lib.filterAttrs (_: hive: hive.certFingerprint != null) swarmCfg.hives - ); in { options.services.hyperhive.swarm.hives = lib.mkOption { @@ -207,33 +200,6 @@ in silently excluded from `wg-hive`). ''; }; - - # Removed, and re-declared only so an existing definition - # produces an error that says *what happened*. Without it the - # module system says `The option ... does not exist`, which - # tells an operator nothing about why it went or what replaced - # it — and this field was set on real deployments. - # - # ⚠️ `lib.mkRemovedOptionModule` cannot do this job here, and - # neither of its two halves survives inside a submodule: - # its `apply = throw` fires only when the value is READ, and - # nothing reads this (that being the point of removing - # it); its `config.assertions` half would land on a submodule - # that declares no `assertions` option at all. It is a - # top-level tool. The assertion below is the working shape, and - # it is the same one ./swarm-peers-removed.nix uses for the - # analogous `peers..caCert`. - certFingerprint = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - visible = false; - internal = true; - description = '' - Removed. Trust inside a swarm comes from the secret store's - pki root (docs/swarm/ca.md), which replaces per-hive leaf - pinning entirely. - ''; - }; }; } ) @@ -388,37 +354,6 @@ in Declared hives: ${lib.concatStringsSep ", " (lib.attrNames swarmCfg.hives)} ''; } - { - # An error rather than a warning, deliberately. Re-declaring the - # removed option above is what stops "option does not exist"; on - # its own it would turn a config that sets the removed field into - # one that quietly evaluates with the setting ignored, which is a - # worse answer than an error. So this stays fatal until the field - # is gone from the config. - # - # Wording follows `lib.mkRemovedOptionModule`'s, so it reads like - # every other removal the module system reports. - assertion = pinnedHives == [ ]; - message = '' - The option definition `services.hyperhive.swarm.hives..certFingerprint' - no longer has any effect; please remove it. - - Still set on: ${lib.concatStringsSep ", " pinnedHives} - - It pinned a peer hive's TLS leaf for hive-c0re's own peer HTTPS - checks, and was removed along with the dashboard feature it - existed to serve — nothing else ever consumed it. - - There is no replacement, and none is needed for a hive inside - this swarm: trust comes from the secret store's pki root (see - docs/swarm/ca.md), which every hive chains to, so one anchor - replaces per-hive pinning. - What that genuinely drops is trusting a hive whose root this - swarm does NOT own — another swarm's, or one keeping its own - CA. That is a cross-swarm problem and wants a mechanism - designed for it. - ''; - } (nameGuards.mustBeIdent { option = "services.hyperhive.swarm.hives"; names = hiveNames; diff --git a/nix/module-eval/builder.nix b/nix/module-eval/builder.nix index 086862eb..5faa1fba 100644 --- a/nix/module-eval/builder.nix +++ b/nix/module-eval/builder.nix @@ -37,7 +37,7 @@ let m: lib.any (a: !a.assertion && lib.hasInfix "deploy.builder.enable requires" a.message) m.assertions; - natsHere = shared.natsOldPath; + natsHere = shared.natsQueueOnly; autheliaHere = hive { deploy.bao.enable = true; diff --git a/nix/module-eval/core-toggle.nix b/nix/module-eval/core-toggle.nix index 15d4f69b..ef766e2c 100644 --- a/nix/module-eval/core-toggle.nix +++ b/nix/module-eval/core-toggle.nix @@ -995,36 +995,6 @@ let && bare.services.dnsmasq.enable && bare.networking.bridges ? hive-br0; } - { - # A host config written against either old spelling still runs its hive. - # The builder's own `true` is lowered to `mkDefault false`, so only the - # forwarded definition can turn it on. - name = "the old hive toggles forward to deploy.hive-controller.enable and warn"; - ok = - let - renamedFrom = - old: m: - lib.any ( - w: lib.hasInfix old w && lib.hasInfix "services.hyperhive.deploy.hive-controller.enable" w - ) m.warnings; - viaOld = - extra: - hive ( - extra - // { - deploy.hive-controller.enable = lib.mkDefault false; - } - ); - oldEnable = viaOld { enable = true; }; - oldC0re = viaOld { c0re.enable = true; }; - in - oldEnable.services.hyperhive.deploy.hive-controller.enable - && oldEnable.systemd.services ? hive-c0re - && renamedFrom "services.hyperhive.enable" oldEnable - && oldC0re.services.hyperhive.deploy.hive-controller.enable - && oldC0re.systemd.services ? hive-c0re - && renamedFrom "services.hyperhive.c0re.enable" oldC0re; - } { # Refused wherever something turns the name into an identifier, and only # there. The store host is the control that the refusal is not the hive's diff --git a/nix/module-eval/forge-placement.nix b/nix/module-eval/forge-placement.nix index a6cf76e0..6c992731 100644 --- a/nix/module-eval/forge-placement.nix +++ b/nix/module-eval/forge-placement.nix @@ -82,16 +82,6 @@ let deploy.hive-controller.enable = false; deploy.forgejo.ci.enable = true; }; - # The older spelling, under the swarm-level forge options. - ciUnderSwarmForge = hive { - swarm.forge.ci.enable = true; - swarm.forge.ci.name = "older-runner"; - swarm.forge.ci.concurrency = 2; - swarm.forge.ci.labels = [ "nix:host" ]; - swarm.forge.ci.package = pkgs.hello; - deploy.bao.ciRunnerClientCertFile = "/etc/pki/bao-ci-runner.pem"; - deploy.bao.ciRunnerClientKeyFile = "/etc/pki/bao-ci-runner-key.pem"; - }; runsForge = m: m.services.hyperhive.deploy.forgejo.enable && m.containers ? hive-forge; @@ -299,22 +289,6 @@ let && lib.any (lib.hasInfix "services.hyperhive.deploy.forgejo.ci.enable") ciUnderForge.warnings && !(lib.any (a: !a.assertion) ciUnderForge.assertions); } - { - # The control is the container: it only exists if `enable` arrived. - name = "a CI option under swarm.forge sets the same option under deploy.ci-runner"; - ok = - let - r = ciUnderSwarmForge.services.hyperhive.deploy.ci-runner; - in - r.enable - && r.name == "older-runner" - && r.concurrency == 2 - && r.labels == [ "nix:host" ] - && r.package == pkgs.hello - && ciUnderSwarmForge.containers ? swarm-ci-runner - && lib.any (lib.hasInfix "services.hyperhive.swarm.forge.ci.enable") ciUnderSwarmForge.warnings - && !(lib.any (a: !a.assertion) ciUnderSwarmForge.assertions); - } { # The rename carries `enable` but not the identity: an old config with # only the old name is refused, and the message says what to set. The diff --git a/nix/module-eval/grafana.nix b/nix/module-eval/grafana.nix index 6732086f..b2dca213 100644 --- a/nix/module-eval/grafana.nix +++ b/nix/module-eval/grafana.nix @@ -22,12 +22,12 @@ let runGroup ; - grafanaOldPath = hive { + grafanaHere = hive { deploy.grafana.enable = true; - swarm.grafana.datasourceUrl = "http://127.0.0.1:19999"; - swarm.grafana.logsDatasourceUrl = "http://127.0.0.1:19998"; - swarm.grafana.plugins = [ ]; - swarm.grafana.package = pkgs.emptyDirectory; + deploy.grafana.datasourceUrl = "http://127.0.0.1:19999"; + deploy.grafana.logsDatasourceUrl = "http://127.0.0.1:19998"; + deploy.grafana.plugins = [ ]; + deploy.grafana.package = pkgs.emptyDirectory; }; # A host that tries to move the socket directory; reading the option throws. @@ -120,7 +120,7 @@ let ok = !(builtins.tryEval grafanaSocketDirSet.services.hyperhive.deploy.grafana.socketDir).success && - grafanaOldPath.services.nginx.virtualHosts.${grafanaOldPath.services.hyperhive.swarm.grafana.domain}.locations."= /metrics".proxyPass + grafanaHere.services.nginx.virtualHosts.${grafanaHere.services.hyperhive.swarm.grafana.domain}.locations."= /metrics".proxyPass == "http://unix:/run/swarm-grafana/grafana.sock:/metrics"; } { @@ -132,8 +132,8 @@ let name = "grafana's published vhost serves /metrics only behind the collector's auth subrequest"; ok = let - g = grafanaOldPath.services.hyperhive.swarm.grafana; - locs = grafanaOldPath.services.nginx.virtualHosts.${g.domain}.locations; + g = grafanaHere.services.hyperhive.swarm.grafana; + locs = grafanaHere.services.nginx.virtualHosts.${g.domain}.locations; checksOf = l: lib.filter (lib.hasPrefix "auth_request") (lib.splitString "\n" l.extraConfig); authz = locs."= /__grafana_metrics_authz"; in @@ -143,7 +143,7 @@ let && !(lib.hasInfix "error_page" locs."= /metrics".extraConfig) && authz.proxyPass == "https://auth.t.local/api/authz/auth-request" && lib.hasInfix "internal;" authz.extraConfig - && !(grafanaOldPath.services.nginx.virtualHosts ? "grafana-metrics") + && !(grafanaHere.services.nginx.virtualHosts ? "grafana-metrics") && lib.hasPrefix "http://unix:" locs."/".proxyPass; } { @@ -152,8 +152,7 @@ let # this host". Its vhost is on the public gateway, so a password box there # is a way in whatever the topology. name = "grafana disables its local login form even where authelia is not on this host"; - ok = - grafanaOldPath.containers.swarm-grafana.config.services.grafana.settings.auth.disable_login_form; + ok = grafanaHere.containers.swarm-grafana.config.services.grafana.settings.auth.disable_login_form; } { # The absence class this whole file is for, and the reported defect in one @@ -445,7 +444,7 @@ let ok = let sources = - grafanaOldPath.containers.swarm-grafana.config.services.grafana.provision.datasources.settings.datasources; + grafanaHere.containers.swarm-grafana.config.services.grafana.provision.datasources.settings.datasources; logs = lib.head (lib.filter (d: d.uid == "swarm-victorialogs") sources); rules = logs.jsonData.logLevelRules or [ ]; # `overwrite_text` makes the stored text the OpenTelemetry short name @@ -473,7 +472,7 @@ let ok = let groups = - grafanaOldPath.containers.swarm-grafana.config.services.grafana.provision.alerting.rules.settings.groups; + grafanaHere.containers.swarm-grafana.config.services.grafana.provision.alerting.rules.settings.groups; rules = lib.concatMap (g: g.rules) groups; rule = lib.findFirst (r: r.uid == "hh-agent-turn-failed") null rules; expr = (lib.head rule.data).model.expr; @@ -517,9 +516,8 @@ let name = "grafana's vhost is rendered with the gateway's listen set, TLS and security headers"; ok = let - gw = grafanaOldPath.services.hyperhive.gateway; - v = - grafanaOldPath.services.nginx.virtualHosts.${grafanaOldPath.services.hyperhive.swarm.grafana.domain}; + gw = grafanaHere.services.hyperhive.gateway; + v = grafanaHere.services.nginx.virtualHosts.${grafanaHere.services.hyperhive.swarm.grafana.domain}; # nginx's `listen` entries come back with defaults filled in # (`extraParameters`, `proxyProtocol`), so compare the fields set. listenOf = map (l: { diff --git a/nix/module-eval/lib.nix b/nix/module-eval/lib.nix index e383507a..2b6826f6 100644 --- a/nix/module-eval/lib.nix +++ b/nix/module-eval/lib.nix @@ -103,13 +103,7 @@ let ]; }).config; - # Note `hyperhive`, not `services.hyperhive`: the agent tier's options moved - # under `services.hyperhive.agent`, and ../agent-modules/renamed-options.nix - # keeps the top-level spelling reaching them. ⚠️ That shim covers the - # options that existed when the tier moved and nothing since, so a fixture - # for an option added afterwards has to go through [`agentWith`] and name - # the real path. - agent = extra: agentWith { hyperhive = extra; }; + agent = extra: agentWith { services.hyperhive.agent = extra; }; baoNames = machine: machine.services.hyperhive.gateway.localNames; diff --git a/nix/module-eval/nats-authelia.nix b/nix/module-eval/nats-authelia.nix index 0a8ad384..2aa84067 100644 --- a/nix/module-eval/nats-authelia.nix +++ b/nix/module-eval/nats-authelia.nix @@ -22,25 +22,18 @@ let runGroup ; - # The queue's two old `swarm.nats` paths that still rename; dropping either - # shim fails the eval. No store identity, which the first case below relies on. - natsOldPath = shared.natsOldPath; + # No store identity, which the first case below relies on. + natsQueueOnly = shared.natsQueueOnly; - # Seventh split slice, plus slice 10's two authelia packages. Of slice 7's - # movers only `usersFile` has a rename entry — the other two are `readOnly`, - # and a rename module contributes a definition, which a read-only option - # refuses; see ./host-modules/deploy.nix. `package` and `bridgePackage` are - # ordinary options, so they do carry one. The two arms - # below have different jobs. `usersFile` tests the rename; the nats one tests - # that a reader pointed at the `deploy` namespace still renders the derived - # path — seven of those reads go through an alias a path-shaped grep - # cannot see. - autheliaOldPath = hive { + # The IdP and the queue on one host. A reader of authelia's host paths has to + # follow the `deploy` namespace; seven of those reads go through an alias a + # path-shaped grep cannot see. + autheliaAndNats = hive { deploy.authelia.enable = true; deploy.nats.enable = true; - swarm.authelia.usersFile = "/var/lib/test-authelia/users.yml"; - swarm.authelia.package = pkgs.emptyDirectory; - swarm.authelia.bridgePackage = pkgs.emptyDirectory; + deploy.authelia.usersFile = "/var/lib/test-authelia/users.yml"; + deploy.authelia.package = pkgs.emptyDirectory; + deploy.authelia.bridgePackage = pkgs.emptyDirectory; # Renders the publisher, which reads the host client-secret dir. deploy.swarm-secret-publisher.baoClientCertFile = "/etc/pki/publisher.pem"; deploy.swarm-secret-publisher.baoClientKeyFile = "/etc/pki/publisher-key.pem"; @@ -297,9 +290,9 @@ let { name = "authelia and nats declare no scrape target on a host with no collector"; ok = - !(autheliaOldPath.services.hyperhive.deploy.swarm-otel.enable) - && !(autheliaOldPath.services.hyperhive.swarm.otel.scrapeTargets ? authelia) - && !(autheliaOldPath.services.hyperhive.swarm.otel.scrapeTargets ? nats); + !(autheliaAndNats.services.hyperhive.deploy.swarm-otel.enable) + && !(autheliaAndNats.services.hyperhive.swarm.otel.scrapeTargets ? authelia) + && !(autheliaAndNats.services.hyperhive.swarm.otel.scrapeTargets ? nats); } { # The xkey and the responder's OIDC client secret are read only from @@ -308,7 +301,7 @@ let # minted one. name = "a queue with no responder store identity is refused at eval, one with a leaf is not"; ok = - refusedWithoutIdentity natsOldPath + refusedWithoutIdentity natsQueueOnly && !(refusedWithoutIdentity natsWithStoreIdentity) && !(refusedWithoutIdentity natsOnStoreHost); } @@ -327,10 +320,10 @@ let # this key would still evaluate cleanly — the only reader that would # notice is a publisher whose row exceeds upstream's much smaller # default, and by then it is a dropped row, not an eval failure. - # Piggybacks on the pre-rename nats fixture above, which already + # Piggybacks on the queue-only fixture above, which already # renders this container's full config. name = "the queue's payload ceiling is set, not inherited from the server's default"; - ok = natsOldPath.containers.swarm-nats.config.services.nats.settings.max_payload == 8388608; + ok = natsQueueOnly.containers.swarm-nats.config.services.nats.settings.max_payload == 8388608; } { # What keeps a callout request inside one principal: the responder is @@ -351,7 +344,7 @@ let && !(settings ? system_account) ) [ - natsOldPath + natsQueueOnly natsOnStoreHost ]; } @@ -375,7 +368,7 @@ let && !(c.services.nats.settings.authorization.auth_callout ? xkey) ) [ - natsOldPath + natsQueueOnly natsOnStoreHost ]; } @@ -384,7 +377,7 @@ let # responder with it: the responder requires the server, so a server that # failed to start left the responder inactive rather than failed. name = "starting the queue's server starts its responder"; - ok = lib.elem "nats.service" (responderOf natsOldPath).wantedBy; + ok = lib.elem "nats.service" (responderOf natsQueueOnly).wantedBy; } { # The keys are created before anything needs them, by the responder's @@ -480,7 +473,7 @@ let ok = let exec = - natsOldPath.containers.swarm-nats.config.systemd.services.swarm-nats-auth.serviceConfig.ExecStart; + natsQueueOnly.containers.swarm-nats.config.systemd.services.swarm-nats-auth.serviceConfig.ExecStart; in !(lib.hasInfix "--agent-publish-subject " exec); } @@ -491,7 +484,7 @@ let name = "the responder grants a verified agent its own hive-free subjects"; ok = let - exec = (responderOf natsOldPath).serviceConfig.ExecStart; + exec = (responderOf natsQueueOnly).serviceConfig.ExecStart; in lib.hasInfix "--agent-token-publish-subject '$$SWARM.term.{agent}'" exec && lib.hasInfix "--agent-token-publish-subject '$$SWARM.agent-state.{agent}'" exec @@ -503,7 +496,7 @@ let name = "the responder grants a verified agent its own icon key"; ok = let - exec = (responderOf natsOldPath).serviceConfig.ExecStart; + exec = (responderOf natsQueueOnly).serviceConfig.ExecStart; in lib.hasInfix "--agent-token-publish-subject '$$KV.agent-icons.{agent}'" exec; } @@ -514,7 +507,7 @@ let name = "a verified agent's grant is exactly its own subjects, and its JetStream API subjects read its own schedule, inference and permissions keys"; ok = let - args = lib.splitString " " (responderOf natsOldPath).serviceConfig.ExecStart; + args = lib.splitString " " (responderOf natsQueueOnly).serviceConfig.ExecStart; granted = lib.concatLists ( lib.imap0 ( i: a: @@ -685,7 +678,7 @@ let # through an `autheliaCfg` alias, where a path-shaped grep does not see it. name = "a consumer of authelia's host client-secret dir renders it from the deploy namespace"; ok = - autheliaOldPath.systemd.paths.swarm-secret-publish.pathConfig.PathChanged + autheliaAndNats.systemd.paths.swarm-secret-publish.pathConfig.PathChanged == "/var/lib/nixos-containers/swarm-authelia/var/lib/authelia-swarm/oidc-clients"; } { diff --git a/nix/module-eval/shared.nix b/nix/module-eval/shared.nix index 5ddf6fb3..cf0ca91b 100644 --- a/nix/module-eval/shared.nix +++ b/nix/module-eval/shared.nix @@ -55,11 +55,10 @@ in # shape that hives elsewhere join. swarmServicesWithController = hiveWithController { deploy.allSwarmServices = true; }; - # The queue's two old `swarm.nats` paths that still rename, `enable` and - # `authPackage`, with no store identity. - natsOldPath = hive { - swarm.nats.enable = true; - swarm.nats.authPackage = pkgs.emptyDirectory; + # The queue with no store identity. + natsQueueOnly = hive { + deploy.nats.enable = true; + deploy.nats.authPackage = pkgs.emptyDirectory; }; controllerAlone = hive { deploy.swarm-controller.enable = true; }; diff --git a/nix/module-eval/swarm-otel-identity.nix b/nix/module-eval/swarm-otel-identity.nix index c970a2b8..26e575d2 100644 --- a/nix/module-eval/swarm-otel-identity.nix +++ b/nix/module-eval/swarm-otel-identity.nix @@ -61,11 +61,6 @@ let # authelia somewhere else, the credential delivered by hand. Whether this # collector authenticates must follow the credential, never another # service's placement. - # - # The `swarm.otel.clientSecretFile` below is the PRE-RENAME path. It predates - # the split and is deliberately left spelled that way: it makes this fixture - # the old-path case for that option too, so dropping its rename entry fails - # the eval here rather than only in a real operator's config. otelRemoteAuthelia = hive { deploy.swarm-otel.enable = true; deploy.authelia.enable = false; @@ -74,7 +69,7 @@ let # `token_url`, so a hive with a secret and no URL has a credential it can # present nowhere. swarm.authelia.url = "https://auth.example.invalid"; - swarm.otel.clientSecretFile = "/var/lib/swarm-otel-oidc/by-hand.secret"; + deploy.swarm-otel.clientSecretFile = "/var/lib/swarm-otel-oidc/by-hand.secret"; }; # A collector whose ONLY scrape work is published: loopback targets forced @@ -148,10 +143,8 @@ let { # The collector's half of the same split, and a different arm from the # authenticator case below: this one reads the PATH the unit loads, so a - # reader left on a source that is non-null but wrong still fails. The - # fixture spells the option its pre-rename way, so it covers the rename - # entry at the same time. - name = "a config written against the pre-rename otel secret path still loads it as a credential"; + # reader left on a source that is non-null but wrong still fails. + name = "a by-hand otel secret path is the one the collector loads as a credential"; ok = lib.any (c: lib.hasInfix "/var/lib/swarm-otel-oidc/by-hand.secret" c) otelRemoteAuthelia.containers.swarm-otel.config.systemd.services.opentelemetry-collector.serviceConfig.LoadCredential; diff --git a/nix/module-eval/swarm-services-switch.nix b/nix/module-eval/swarm-services-switch.nix index be3f35fe..793c852b 100644 --- a/nix/module-eval/swarm-services-switch.nix +++ b/nix/module-eval/swarm-services-switch.nix @@ -124,7 +124,7 @@ let deploy.forgejo.enable = true; deploy.forgejo.mirrors = [ aMirror ]; deploy.forgejo.actionsFromSelf = false; - c0re.orgAvatarPng = "/etc/marker-avatar.png"; + deploy.swarm-controller.configOrgAvatarPng = "/etc/marker-avatar.png"; }; mirrorsNoController = hive { deploy.forgejo.enable = true; @@ -796,7 +796,7 @@ let && lib.elem "actions/checkout" (map (m: m.dest) (mirrorsOf forgeWithController)); } { - name = "the old c0re org-avatar option lands on the controller's unit"; + name = "the org avatar lands on the controller's unit, not hive-c0re's"; ok = controllerWithMirrors.systemd.services.swarm-controller.environment.SWARM_CONTROLLER_CONFIG_ORG_AVATAR_PNG == "/etc/marker-avatar.png" diff --git a/nix/packages/claude-plugins.nix b/nix/packages/claude-plugins.nix index b83896a0..b60d7c0f 100644 --- a/nix/packages/claude-plugins.nix +++ b/nix/packages/claude-plugins.nix @@ -4,7 +4,7 @@ # hyperhive-authored Claude Code plugin marketplace (source tree at # ../../claude-plugins), shipped as a standalone derivation and spliced -# into the *default* `hyperhive.claudeMarketplaces`/`claudePlugins` lists +# into the *default* `services.hyperhive.agent.claudeMarketplaces`/`claudePlugins` lists # (nix/agent-modules/claude-settings.nix) as a local marketplace source. # `claude plugin marketplace add` accepts a plain filesystem path (verified: # `claude plugin marketplace add --help` → "Add a marketplace from a URL, diff --git a/nix/packages/default.nix b/nix/packages/default.nix index ada5f3c2..3601539c 100644 --- a/nix/packages/default.nix +++ b/nix/packages/default.nix @@ -244,7 +244,7 @@ in assets = pkgs.callPackage ./assets.nix { }; # hyperhive-authored Claude Code plugin marketplace (source tree at # ../../claude-plugins) — a local-path marketplace spliced into the - # default `hyperhive.claudeMarketplaces`/`claudePlugins` lists. See + # default `services.hyperhive.agent.claudeMarketplaces`/`claudePlugins` lists. See # ./claude-plugins.nix for the "why a store path, not a repo" rationale. claude-plugins = pkgs.callPackage ./claude-plugins.nix { }; # The repo docs/ markdown tree as a standalone derivation — diff --git a/nix/packages/frontend.nix b/nix/packages/frontend.nix index 4aaace02..c31b3d45 100644 --- a/nix/packages/frontend.nix +++ b/nix/packages/frontend.nix @@ -20,7 +20,7 @@ # static/{tabs,flow,stats,stream-worker}.js{,.map} # static/{colors,theme,common,dashboard,flow,stats}.css # $out/agent/ the per-agent default UI (layered with -# hyperhive.frontend.extraFiles at activation time) +# services.hyperhive.agent.frontend.extraFiles at activation time) # index.html stats.html screen.html # static/{app,stats}.js{,.map} # static/{colors,theme,agent}.css diff --git a/nix/sources.nix b/nix/sources.nix index 90083ee9..2fd4ad14 100644 --- a/nix/sources.nix +++ b/nix/sources.nix @@ -15,7 +15,7 @@ # frontend/, prompts/, flake.lock. docs/ is deliberately dropped: # it is shipped to agent containers as its OWN narrow meta-flake # input (`hyperhiveDocsSource`, below), threaded through hive-c0re - # → the meta flake → `hyperhive.docs.source`. Keeping docs/ out of + # → the meta flake → `services.hyperhive.agent.docs.source`. Keeping docs/ out of # THIS source means a doc edit only re-hashes the docs input (a # cheap re-link), not the whole flake source (which would rebuild # every agent container). The `reference-docs` build from `../docs` @@ -39,7 +39,7 @@ # path moves ONLY on doc edits, decoupled from `hyperhiveFlakeSource`. # hive-c0re threads this to the meta flake as the `hyperhive-docs` # input (same pattern as the `hyperhive` input); the harness resolves - # `$HIVE_DOCS_DIR` from it via `hyperhive.docs.source`. Evaluated + # `$HIVE_DOCS_DIR` from it via `services.hyperhive.agent.docs.source`. Evaluated # here on the host where docs/ exists — it cannot be derived from # inside the docs-stripped `hyperhiveFlakeSource`. hyperhiveDocsSource = lib.cleanSourceWith {