Commit graph

2,666 commits

Author SHA1 Message Date
atlas
de69a9f02c refactor(#2441): live-log header shows the streaming node's agent, not the DAG's
Review (iris/argus): the dashboard rebuild live-log header labels one
specific node's log stream (liveNode, keyed by its build_log_id), so it
should show that node's own agent — entryAgents(running) listed every
agent in the DAG, which would mislabel a single agent's log once DAGs
span multiple. The other entryAgents() sites (row label, cancel-confirm,
fingerprint) are correct whole-DAG summaries and unchanged.
2026-07-14 21:58:40 +02:00
atlas
e2b48d2014 refactor(#2441): update queue consumers + docs for agent-per-node
DagView no longer has a DAG-level agent, so consumers derive it from the
per-node agents:
- hivectl dag_progress.rs: dag_agents(d) helper (distinct node agents,
  comma-joined) in place of d.agent.
- dashboard builds.js: entryAgents(entry) helper likewise for the
  rebuild-queue card + live-log header + cancel confirms.
- docs/coordinator.md: lease prose (node-agent-keyed, global per agent),
  wire shape (NodeView.agent, no DagView.agent), and dropped the removed
  dedup section.
2026-07-14 21:58:40 +02:00
atlas
2a59f2f5fc refactor(#2441): move agent field from DAG onto Node; drop dedup
Agent was a single field on Dag/DagSpec, making a DAG structurally
one-agent — a multi-agent op could only ever be N separate DAGs. Move it
onto Node/NodeSpec (and the NodeView wire type), drop it from Dag/DagSpec
(and DagView): a DAG can now span agents.

- lifecycle lease keys on the node's agent, still globally exclusive per
  agent across all DAGs (Inner.leases unchanged in shape). A DAG holds one
  lease per distinct agent it touches; settle() frees each at DAG-terminal
  (per-agent-subgraph early release is a follow-up, only observable with
  multi-agent DAGs).
- transient guard keyed (dag_id, agent); cancel-revert + Rebuilt events
  walk TerminalDag.agents.
- submit-time dedup removed (a multi-agent DAG has no single agent to key
  on); every submit enqueues a fresh DAG. Whether dedup needs reintroducing
  is tracked in a follow-up sub-issue.
- templates gain a node(agent, kind, deps) helper stamping the agent onto
  every node; meta templates stamp "hyperhive".

Templates stay single-agent in this PR — behaviour is unchanged, only the
representation + wire shape. Multi-agent DAG emission (restart/restart-all/
broad stop+start as one DAG) and the SetWanted-as-a-node change are
follow-ups off #2439.
2026-07-14 21:58:40 +02:00
damocles
91607f3896 refactor(#2442): split harness-state persistence out of events.rs 2026-07-14 21:15:37 +02:00
damocles
bf657177d3 fix(#2438): link config approval commit by full sha, not abbreviated 2026-07-14 20:48:35 +02:00
atlas
407965b6e1 fix(#2398): correct stale doc on handle_restart_scoped
argus caught it: the function-level /// comment still described the
old submit-await-submit graceful approach after the code moved to
one atomic GracefulRestart DAG.
2026-07-14 20:38:10 +02:00
atlas
901ab6a779 fix(#2398): graceful restart as one atomic DAG, not compose-and-await
mara's review on #2436: no submit-await-submit composition, even
server-side. Adds Template::GracefulRestart (Signal -> Drain ->
StopForUpdate -> Reconcile, wanted=Up) mirroring how Restart already
does StopForUpdate -> Reconcile, plus submit::graceful_restart and
templates::graceful_restart. handle_restart_scoped now submits exactly
one DAG per agent up front for both the graceful and non-graceful
case -- no await_dags in the loop anymore.
2026-07-14 20:38:10 +02:00
atlas
4cfa040154 feat(#2398): dagify hivectl restart (RestartScoped)
hivectl restart --agent NAME previously composed stop() then start() as
two separate client-side daemon calls glued by CLI-process control flow
— not one DAG, and a dropped hivectl connection mid-restart (ssh drop,
Ctrl-C) left the agent stopped with no automatic follow-up. mara flagged
this as the first target for the 'dagify hivectl commands' issue.

New HostRequest::RestartScoped{scope, graceful} handles it server-side:
each targeted agent now rides exactly one atomic Restart-template DAG
(same one hivectl agents restart / restart-all already use) in the
common non-graceful case. --graceful has no single-DAG template yet, so
it submits the graceful-stop DAGs, awaits them server-side, then submits
the start DAGs — still one daemon call end to end, just not yet a single
DAG (noted as a follow-up). Infra containers restart synchronously as
before (no lease/DAG concept for them).

CLI-side restart() now just makes the one call + waits, same output
shape as before via render_lifecycle.
2026-07-14 20:38:10 +02:00
damocles
5853ce2c8d fix(#2417): hardcode external,gateway in forgejo webhook allowlist, drop option 2026-07-14 20:26:02 +02:00
damocles
60e4ea8bb8 fix(#2417): keep 'external' in webhook allow-list so public webhooks still deliver 2026-07-14 20:19:49 +02:00
damocles
00c9a15ae6 fix(#2417): make webhook allow-list additive via forge.webhookAllowedHosts option 2026-07-14 20:16:12 +02:00
damocles
cada704856 fix(#2417): allow gateway host in forgejo webhook ssrf list so config-pr webhook delivers 2026-07-14 20:07:27 +02:00
atlas
e0461e1af6 refactor(#2431): extract hive-priv-sock crate from hive-sh4re
Split the priv-socket wire types (PrivRequest/PrivResponse/PrivEvent and
friends) out of hive-sh4re into their own hive-priv-sock crate, mirroring
the existing hive-host-sock split. hive-priv — the root-privileged
helper — now depends on just this narrow protocol crate instead of the
much larger daemon-shared crate, shrinking its dependency surface and
making the privsep boundary easier to audit. No server/client
implementation lives here, only the wire contract; hive-c0re still
depends on hive-sh4re directly for everything else.
2026-07-14 20:02:20 +02:00
damocles
ffde085c35 fix(#2433): mkforce container@hive-ci timeoutstartsec to resolve nixpkgs conflict 2026-07-14 19:59:32 +02:00
iris
46bdb78466 hive-forge: validate --forge <label> charset up front
argus flagged (approving) that an unvalidated label could build a path
outside the state dir; mara called it out as a usability issue in its
own right, not just a low-risk security nit — a typo'd label should
give a precise 'not a valid label' error, not a confusing file-not-found
or an unexpected traversal.

Reject anything outside the plain lowercase+digits+hyphens charset
dashboard/extra_forges.rs already enforces on write, before touching
the filesystem at all.
2026-07-14 19:37:20 +02:00
iris
8e58793f79 hive-forge: add -f/--forge <label> selector for external forge accounts
Targets a dashboard-provisioned external forge account (FORGES tab)
instead of the internal forge: resolves `forge-<label>-token` for the
token and `forge-<label>.json`'s base_url for the URL, the same two
files dashboard/extra_forges.rs writes, instead of
HIVE_FORGE_URL/forge-token. Falls back to today's behavior when unset.
Orthogonal to -r/--repo.

An unknown label gives a clear error listing the labels actually found
in the state dir instead of a raw file-not-found. The base_url JSON key
is read via a typed sidecar struct pinned to what extra_forges.rs
writes, so the read side can't silently drift from the write side.
2026-07-14 19:31:08 +02:00
atlas
60a253a2f6 fix(#1763): address argus review on PR #2430
- regenerate docs/tools/hivectl-cli.md for the new `subvol snapshot send` verb
- close the TOCTOU on the no-overwrite guard: File::options().create_new(true)
  (O_CREAT|O_EXCL) instead of exists()-then-create, so the guarantee is
  atomic against a concurrent request racing the same dest filename
- warn (not silently swallow) if cleaning up a partial export after a
  failed btrfs send itself fails, so a stuck garbage file masquerading
  as a completed export is visible in the log
2026-07-14 19:23:59 +02:00
atlas
90af0edab0 feat(#1763): local-file half of btrfs send/receive migration transport
SendAgentSnapshotToFile priv op: btrfs send [-p <parent>] <snapshot> to a
file under MIGRATE_STAGING_ROOT. Standalone-useful as a point-in-time
snapshot export/backup today; the cross-hive ssh-piped leg (auth/trust
design posted on #1763, awaiting mara/damocles steer) is a later,
separate piece this doesn't block on.

- hive-sh4re: PrivRequest::SendAgentSnapshotToFile + MIGRATE_STAGING_ROOT
- hive-priv: validates names, refuses to overwrite an existing export,
  cleans up a partial file on btrfs send failure
- hive-c0re: priv_client::send_agent_snapshot_to_file
- hivectl: `hivectl subvol snapshot send <agent> <label> [--parent <label>] --dest <file>`
2026-07-14 19:23:59 +02:00
damocles
f5c8a6a8db fix(#2426): bash wake gives status + read() pointer, not inline stdout/stderr 2026-07-14 19:20:27 +02:00
iris
5869f34390 dashboard: fix stale nix/modules path in extra_forges.rs doc comment
nix/modules/ was restructured into nix/host-modules/ + nix/agent-modules/
before the extra-forges branch merged; this doc-only fix from PR #2422
(e53f70ac) landed after the merge went through and got dropped. Reapplying
directly against main.
2026-07-14 19:00:03 +02:00
atlas
2c079afd65 fix(#2391): drop "." entirely from credential/snapshot name charset
Per mara: "i would have even disallowed ., we are making up the rules
here lets go strict". validate_credential_name now restricts to
[A-Za-z0-9_-] (no dot at all) instead of [A-Za-z0-9_.-] + a separate
".." substring check — simpler rule, and there's no legitimate need
for a dot in either a systemd credential id or a hive- prefixed
snapshot label. Matching hivectl client-side check + wire-proto doc
comments updated.
2026-07-14 18:54:31 +02:00
atlas
720ac81235 fix(#2391): reject ".." in credential/snapshot names (path-traversal hardening)
Per mara's review: validate_credential_name allowed any [A-Za-z0-9_.-]
byte sequence, which permits a literal ".." substring. Not currently
exploitable (snapshot_path() embeds the label inside a single
format!()'d path component with no "/" in the allowed charset, so
there's no directory to traverse into), but it's a landmine for any
future caller that builds a path via PathBuf::from(name) directly
instead of the current string-embedding. Reject ".." outright in the
shared validator, plus a matching client-side check in hivectl for
fail-fast UX (hive-priv's copy is still the authoritative one).
2026-07-14 18:54:31 +02:00
atlas
8f8076b8ed fix(#2391): mandatory hive- prefixed snapshot label, nest subvol snapshot create/delete
Per mara's PR review:
- snapshot label is now mandatory (was optional w/ timestamp default)
  and must start with "hive-" — hive-priv enforces this as an
  allow-list on top of the existing credential-name charset check, so
  only hivectl-issued labels can reach the btrfs shellout.
- nest under `subvol snapshot create`/`subvol snapshot delete`
  instead of othering delete as a separate top-level `delete-snapshot`
  verb.

Per argus's review:
- regenerate docs/tools/hivectl-cli.md (hivectl markdown-docs) to
  include the new subcommands — CI's hivectl-docs-fresh check compares
  this file against generated output.
2026-07-14 18:54:31 +02:00
atlas
7799e0762a feat(#2391): btrfs SnapshotAgentSubvolume/DeleteAgentSnapshot priv ops
Adds the first missing piece from #2391's migration-gaps list: a
read-only btrfs snapshot priv op so hivectl migrate can freeze a
consistent point-in-time copy of an agent's state subvolume for
btrfs send, without stopping the live agent.

- PrivRequest::SnapshotAgentSubvolume / DeleteAgentSnapshot (hive-sh4re)
- hive-priv handlers: btrfs subvolume snapshot -r / delete, sibling
  dot-prefixed path (<AGENT_STATE_ROOT>/.<agent>.snapshot.<label>)
- hive-c0re::priv_client wrappers
- hivectl subvol snapshot / delete-snapshot verbs (no agent stop needed
  — btrfs snapshots are atomic against a live subvolume)

Does not yet wire actual btrfs send/receive or the hivectl migrate
verb — those stay tracked on #2391 as separate follow-up pieces.
2026-07-14 18:54:31 +02:00
damocles
0bfe47458c fix(#2401): mirror config-pr deploy tag to forge via push_config 2026-07-14 18:52:11 +02:00
damocles
15e56986ad fix(#2417): trust hive ca in forge container for self-signed webhook delivery 2026-07-14 18:39:11 +02:00
iris
ec24b96022 forge: drop stale TOKEN_SCOPES/extra.rs cross-reference
extra.rs (external-forge account minting) was removed in the
dashboard-provisioned redesign; TOKEN_SCOPES only applies to tokens
hive-c0re mints itself on the internal forge. External forge tokens
are pasted by the operator verbatim, so we never mint them and don't
need to know their scope.
2026-07-14 18:38:55 +02:00
iris
dbf880ac66 extra-forges: fully dashboard-provisioned, no host config
Per mara's feedback on PR #2407 ("better: you can also provide url in
dashboard, same as with matrix, no host config"), drops
services.hyperhive.extraForges and the admin-API mint/revoke flow
entirely. The operator now creates a token on the external forge
themselves and pastes a label + base URL + access token into the
dashboard's FORGES tab, the same shape as the GitHub PAT flow plus the
base-URL field from the matrix extra-account flow. hive-c0re only ever
writes/deletes two local files per account (forge-<label>-token,
forge-<label>.json sidecar for the URL) via hive-priv — no remote
account creation, no admin token, no revoke-on-the-remote-side, no nix
config to enumerate.

- nix/host-modules/hive-forge/default.nix: removed the extraForges
  option, its label-format assertion, and the HYPERHIVE_EXTRA_FORGES
  env forwarding.
- hive-c0re/src/forge/extra.rs: deleted (REST admin-API provisioning,
  no longer needed).
- hive-c0re/src/dashboard/extra_forges.rs: GET /api/extra-forges?
  agent= lists an agent's stored forges by scanning its state dir
  (mirrors matrix_accounts.rs's filename-scan listing), POST
  /api/extra-forge-account (agent/label/base_url/token/
  action=add|remove) stores or removes an account.
- hive-sh4re/priv_proto.rs + hive-priv/main.rs: new
  WriteAgentExtraForgeAccount/DeleteAgentExtraForgeAccount priv
  requests (adds base_url, writes/deletes a JSON sidecar alongside the
  token).
- hive-c0re/src/priv_client.rs: matching wrapper functions.
- frontend/packages/dashboard/src/credentials.{html,js}: FORGES tab is
  a per-agent list + add-account paste form (label/base_url/token), no
  grant/revoke-from-catalog UI.
- docs/web-ui/dashboard.md: FORGES tab section rewritten.

Supersedes the design in PR #2407 (already approved+green on the old
admin-API model) — opening as a fresh PR against the same issues
rather than force-pushing over the approved one.
2026-07-14 18:38:55 +02:00
damocles
f025f32ccb fix(#2417): cancel stale config-pr approvals when the pr is no longer open 2026-07-14 18:10:43 +02:00
atlas
2bf6290aa0 fix(#2410): bump container@hive-ci TimeoutStartSec to survive slow runner registration
gitea-runner registration (hive-ci-prefetch, host-side) sits on the
boot-critical path -- nspawn readiness is gated on the runner
registering, itself a forge + core-token round trip that can wait up
to 60s for the core token. The default ~60s TimeoutStartSec can trip
mid-register (especially right after a .runner purge, since every
boot re-registers from scratch), killing the half-started container
and triggering a restart loop until the token/forge settle. Bump to
180s so one register attempt has room to finish.
2026-07-14 17:38:40 +02:00
müde
0d416df1e8 fix: shared-netns infra containers must not run their own firewall 2026-07-14 00:08:33 +02:00
müde
d97659c06a docs: add the network topology map to network.md 2026-07-13 23:58:54 +02:00
müde
68afbb01f7 fix: per-key mkDefault for hyperhive.packages so single-key overrides merge 2026-07-13 22:48:55 +02:00
müde
eba3f206ab docs: reflect the new nix layout and removed options 2026-07-13 22:23:20 +02:00
müde
79fec131d6 docs: sweep archaeology comments to present state 2026-07-13 22:16:02 +02:00
müde
e0cfed7fe8 refactor: jq for tea-login, build-time avatar png, shared leaf-sign script 2026-07-13 22:14:25 +02:00
müde
935e967718 feat: hive DNS always follows the host resolver 2026-07-13 22:09:53 +02:00
müde
4a48ce5024 refactor: nix/host-modules + nix/agent-modules layout, update doc paths 2026-07-13 22:05:49 +02:00
müde
cb755b677c refactor: hive-forge module dir owns its theme css 2026-07-13 22:02:15 +02:00
müde
7c9d72b9ca refactor: rename templates to agent.nix/ruth.nix, sweep stale name refs 2026-07-13 22:01:24 +02:00
müde
7ad2bb9211 refactor: replace deprecated no-op options with mkRemovedOptionModule 2026-07-13 21:58:30 +02:00
müde
43bbd64f82 refactor: drop dead http-only TLS branches (gateway is always https) 2026-07-13 21:54:30 +02:00
müde
874a900bdc refactor: split c0re module into options/theme/environment, hive-priv standalone 2026-07-13 21:50:32 +02:00
müde
281667d5a8 refactor: thread agent packages via option, drop overlay and forge-tools shim 2026-07-13 21:43:26 +02:00
müde
e7689a6804 refactor: split hive-c0re module, aggregate host stack in nix/modules 2026-07-13 21:37:47 +02:00
müde
5f9af9fc97 refactor: split hive-gateway into module dir, DHCP range into hive-network 2026-07-13 21:26:30 +02:00
müde
ecaad48fad refactor: split harness-base.nix into harness/ feature modules 2026-07-13 21:16:29 +02:00
müde
c0bd77b314 fix: select rust sources via filesets so new dirs keep the cargo cache 2026-07-13 21:16:29 +02:00
müde
3069c11c3d refactor: build workspace once, extract per-bin packages 2026-07-13 21:05:12 +02:00
müde
4f8bb6ded2 refactor: slim flake.nix into nix/ entry files 2026-07-13 20:55:43 +02:00