Per operator directive: a silent automatic force-push is a bug. The
config-repo merge path already never force-pushes (run_merge_config_pr
lands via ff_push_to_main, a non-force git push). So set the branch
protection's enable_force_push to false — main only ever advances by
fast-forward.
The legacy push_config mirror does force-push (it re-points the status
tags and rewinds main on a failed-build rollback); the protection now
rejects those non-ff updates, so the mirror runs best-effort until the
agent-opened PR-merge flow retires it. Docs + comments updated to match.
Address argus review on the config-repo PR surface: 6 issue-number tags
appeared in Rust doc comments in forge.rs; the tracker-tag lint matches
${'#'}NNN in code comments (markdown docs are exempt, doc comments are not).
Reword to prose, no behavior change.
Wires the per-agent config repo as the editable PR surface the #1838
merge handler (run_merge_config_pr) consumes, without yet retiring the
push_config force-mirror (that waits for #1838 P2 — the agent-opens-PR
MCP surface — so config changes keep working through the transition).
ensure_config_repo now, after creating agent-configs/<name>:
- adds the agent as a WRITE collaborator (can push config-change branches
+ open PRs);
- branch-protects main core-only via apply_config_repo_branch_protection:
push + merge whitelists are core-only (only hive-c0re lands on main, via
its verify-and-ff-push handler), operator-team approval required, the
agent can't push main directly or self-merge.
The protection sets enable_force_push=true as a TRANSITIONAL allowance so
push_config's applied->main force-mirror keeps working until P2 retires it
(a protected branch otherwise rejects force-push). Forgejo's force-push
allowlist is deploy-keys-only — no per-user list — so this is a plain
enable_force_push toggle; only core is in the push-whitelist so only core
can force-push anyway. At P2, flip it to false and keep core in the
push-whitelist so ff_push_to_main still lands.
Agent code repos (AGENTS_ORG) are intentionally untouched — their merge
flow has no auto-merge handler yet, so switching them to core-only-merge
would strand those PRs.
All steps idempotent (runs on every spawn + startup sweep). Updated the
CONFIG_ORG doc comment + docs/forge.md to drop the "mirror-only" framing.
Adds a Window::All option to the per-agent stats page and the hive-wide
rollup, selectable as a new 'all' tab on both.
- hive-ag3nt (per-agent, time-bucketed): All ranges from MIN(started_at)
to now (fallback to now on an empty table) with an adaptive bucket
width laddered by span — hourly <=2d, daily <=90d, weekly <=2y, 30-day
beyond — so the trend series stays bounded (~<=104 buckets) at any age.
- hive-c0re (swarm rollup, not time-bucketed): All sets from=0 so the
aggregate covers every recorded turn across all agents.
- frontend: an 'all' button on both the agent stats and dashboard hive
stats window selectors (createTabStrip + fetch already pass the window
string through, so no JS change needed).
cargo check passes on both crates; FE builds clean. Fixes#1919.
Three forge-side fns + a typed error the c0re approve-handler
(run_merge_config_pr, a follow-up) orchestrates to land an operator-approved
config PR:
- pr_head_sha(repo, pr): git ls-remote refs/pull/<pr>/head — the handler's
primary drift gate (compare against the approved sha).
- ff_push_to_main(repo, sha): the merge — ls-remote main, strict-ancestor
pre-check (git merge-base --is-ancestor in the agent's applied repo where
the orchestration has fetched the sha), then a non-force push sha:main. Two
independent guards so a raced main surfaces as NotFastForward rather than
clobbering reviewed history.
- mark_pr_merged(repo, pr, sha): POST pulls/<pr>/merge Do=manually-merged
MergeCommitID=sha; best-effort HeadDrift detection on non-2xx via a PR-head
re-read (the handler's pre-merge re-read is the primary gate).
ForgeMergeError {HeadDrift, NotFastForward, Other} (hand-rolled Display/Error/
From<anyhow::Error>, no new dep) so the handler can match recoverable drift
(refresh + re-verify) vs hard-fail. Core token sourced internally.
Uncalled until the handler lands (pub lib API, no dead_code). clippy -D,
unit tests, and treefmt clean.
The internal forge is the canonical store for the meta flake, every
agent's config repo, and the internal/* repos, so it can no longer be
optional. Remove the services.hyperhive.forge.enable toggle:
- hive-forge.nix: drop the `enable` option; the forge config now
deploys gated on `services.hyperhive.enable` (it ships with hyperhive).
- hive-c0re.nix: HIVE_FORGE_URL env unconditional; forge-public-URL gate
drops the enable check (keeps behindGateway).
- hive-gateway.nix: local /etc/hosts forge entry keyed on behindGateway.
- hive-ci.nix: drop the now-moot `forge.ci.enable requires forge.enable`
assertion (forge is always present); reword the option doc.
- nix/docs/default.nix: drop the `forge.enable = mkForce false` stub
(option gone); the options-doc eval stays light via hyperhive.enable.
- hive-c0re forge.rs / hivectl.rs: reword 'forge.enable = true' error
text to 'wait for hive-c0re to start the container' (the runtime
token-absent path is unchanged — it's a bootstrap-timing check, not
the opt-out).
- docs/approvals.md, docs/ci.md: drop stale forge.enable references.
Migration: configs that set `services.hyperhive.forge.enable = false`
must drop the line — the forge is now mandatory.
Prereq/companion to #1838 (PR-based config flow, which assumes the forge
is always present).
Per argus review: std::env::var(..).ok() yields Some("") for an empty
env value, so require_hive_domain would 'succeed' with an empty domain
and emit invalid nix (swarm.peers."" = …). Filter empty so it resolves
to None → the CLI's clear 'domain unset' error fires instead.
Per operator review (#1899): the hive's own domain should never be
typed by hand. Remove the --domain flag from peer-config and wg init
entirely; both now resolve it from the daemon (HostRequest::HiveDomain).
peer-config errors with a clear hint if the daemon can't answer; wg init
prints the peer block best-effort (skips it, still enables the mesh, if
unresolved). Regenerated docs/tools/hivectl-cli.md.
Per operator review: hivectl shouldn't make the operator retype this
hive's own domain. Add a HostRequest::HiveDomain admin-socket query
(c0re answers from HYPERHIVE_HIVE_DOMAIN, already in its service env) and
a domain field on HostResponse.
hivectl peer-config now resolves the domain as: --domain override (kept
for offline/scripted use), else the daemon query; errors with a clear
hint when neither resolves. wg init resolves it best-effort too, so it
prints the hand-over peer-config block without --domain (skipped, not
failed, when the daemon is unreachable).
Regenerated docs/tools/hivectl-cli.md.
Add `hivectl peer-config --domain <this-hive-domain>`: prints the
`services.hyperhive.swarm.peers."<domain>"` nix block a peer operator
pastes to federate with this hive. Emits `caCert = ./<hive>-ca.pem` plus
a `cp /var/lib/hive-tls/ca.pem ./<hive>-ca.pem` line when this hive
serves a self-signed CA (the cert file exists); omits caCert for
ACME/public-CA hives (trusted by the default bundle). Includes the
WireGuard public key when the mesh key exists, and the
wireguardAddress/Endpoint passed via flags.
`wg init` gains an optional --domain; when set it calls peer-config at
the end, so a fresh mesh setup prints the hand-over block in one command.
Pure output — reads local state (TLS CA cert presence, wg key), never
mutates. Regenerated docs/tools/hivectl-cli.md.
Closes#1897.
Add swarm.peers.<domain>.caCert (path to a peer hive's root CA PEM),
trusted everywhere the hive's own internal CA is — so a self-signed
peer hive can federate (matrix) and any in-hive consumer validates its
certs.
Mechanism (reuses the existing hive-CA embedding): the meta-flake
renderer embeds a LIST of CA files next to each agent's flake —
hive-ca.pem (the hive's own self-signed CA, when active) plus each peer
caCert as peer-ca-<N>.pem — and emits them all in
security.pki.certificateFiles, so every agent trusts them at build
time. The matrix container trusts the same peer CAs for federation TLS.
Nothing is installed in the host trust store; the certs live in the nix
store (no mutable host file).
- meta.rs: embedded_ca_files() = hive CA + peer CAs (from new
HIVE_PEER_CA_PATHS env); ca_embed_state() tracks the list (content +
add/remove); sync_agents materialises + stages the list; render emits
the multi-entry certificateFiles. Tests cover hive-only / hive+peers
/ peers-only / none.
- hive-c0re.nix: HIVE_PEER_CA_PATHS service env (colon-joined caCerts);
caCert / certFingerprint option docs updated to the hive-wide scope.
- hive-matrix.nix + docs/swarm.md: scope + comment updates.
certFingerprint stays the c0re-only leaf-pin path.