Commit graph hyperhive/hive-c0re/src/forge/mod.rs
Author SHA1 Message Date
bitburner
517acc9f33 fix(#3245): resolve broken intra-doc links in hive-c0re
Remove or fix broken documentation links that accumulate silently:
- container_view.rs: HiveEnv reference
- forge/mod.rs: READY_TIMEOUT and webhook handler links
- workers/knowledge.rs: webhook handler link
- job_queue/model.rs: Claim::deps and WireNode::data references
- stats/hive_stats.rs: read_skill_breakdown reference
- stores/audit_log.rs: global() reference
- workers/agent_sockets.rs: ambiguous agent_sockets::write reference
- coordinator.rs: systemd.services.<harness> formatting
- resource_limits.rs: ambiguous write/read references

Some broken links were to deleted functions/types; these are replaced
with prose descriptions. Others referenced items outside this crate or
were private; these are replaced with plain text references or qualified
paths as appropriate.

Fixes: #3245
2026-08-14 00:25:35 +02:00
atlas
44572d1e1a fix(#2911): keep the forge token out of argv
`forge_git_url` spliced `core:<token>@` between scheme and authority, and
that URL is a process argument. `/proc/<pid>/cmdline` is mode 0444 —
world-readable — so the core admin token, which provisions every agent's
forge account, was published to any local user for the lifetime of each
git child. Seven call sites built such a URL.

The credential now travels in the environment instead:
`git_command_authed` sets `http.extraHeader` via `GIT_CONFIG_*`, which
git reads exactly like a config file, and `/proc/<pid>/environ` is 0400 —
owner-only. Same credential, materially smaller audience. The remote is a
plain `http://forge/<org>/<repo>.git`, and `forge_git_url` no longer takes
a token, so the old shape cannot be rebuilt by accident.

`knowledge`'s clone was the one place a credentialed URL was stored as a
named remote — git persists the clone URL into `.git/config`, so the
token sat on disk and every later `pull` authenticated from there. That
is the case `forge::repos::push_config` documents as forbidden ("the
tokenised URL ... deliberately never stored as a named remote"). `pull`
now rewrites `origin` to the plain URL first, which also scrubs the
persisted token from existing deployments, and authenticates from the
environment when a token is available. The repo is public, so the pull
still works without one.

Three call sites also stopped spawning `Command::new("git")` directly,
so they honour the `HYPERHIVE_GIT` path the NixOS module bakes in and
the `kill_on_drop` every other git spawn gets.

The two URL-shape tests now assert the *absence* of a credential, and a
new one decodes the header back to `core:<token>` — without that, a
malformed header would leave every forge operation silently anonymous
with the other assertions still green.
2026-08-02 13:21:42 +02:00
atlas
10285ff764 fix(#2911): name the forgejo admin verb in errors, not its arguments
forge_admin interpolated its whole argument vector into the error
context, and two callers pass a live operator password in that vector
(user create --password, user change-password). Any failure of those
commands wrote the password to hive-c0re's log in cleartext -- and the
likeliest trigger is forgejo rejecting a weak password, so the secret
got logged precisely because forgejo refused it.

Redacting the value after --password would repeat the bug the issue is
about: redact_password_line matched one keyword and a differently named
secret walked past it. A denylist fails open, silently, and the next
secret-bearing flag would leak until someone extended the list.

describe_forge_admin keeps the leading verb path and stops at the first
flag, so "user create --username iris --password ..." is reported as
"forgejo admin user create". The verbs are a closed set this crate
chooses itself; argument values never are, so a new flag is excluded by
construction. Nothing useful is lost -- the context says which operation
failed, and the underlying error already carries forgejo's own message
about why.

The same pattern in hive-priv is deliberately untouched: that crate runs
as root and the redactor's shape is still an open question on the issue.
This change holds under either answer.
2026-08-02 12:24:52 +02:00
atlas
8ad3b8e987 fix(#2860): drop hive-c0re's hardcoded localhost forge fallbacks
Two sites, same class, different blast radius:

`forge_http_base()` fell back to `http://localhost:3000` when
`HIVE_FORGE_URL` was unset. The NixOS module sets that variable
unconditionally, so the fallback could only ever fire for a process
started outside the module — where guessing produces a confusing
"connection refused" far from its cause. It now panics saying exactly
that.

`forge_git_url()` had a second, nastier fallback: a base URL with no
`://` produced `http://core:<token>@localhost:3000/...`, sending a
*credentialed* git push at whatever happened to answer on the local
port. Split the credential-insertion half out as `git_url_with_base`,
which panics on a malformed base.

That split also lets the tests cover the shape without setting a
process-wide env var, which would race every other test in the binary.
Adds a case pinning that the scheme is carried through rather than
assumed — the old hardcoded `http://` would have silently downgraded a
TLS-fronted forge.

Refs #2860
2026-07-31 22:53:59 +02:00
atlas
d4b9dc1ab9 fix(#2570): gate boot forge-provisioning behind a readiness poll
The boot provisioning pass (orgs, repos, teams, CI-runner token) all hits
the Forgejo API, but ensure_all only checked the container is *present*,
not that its HTTP is *listening*. A nixos-rebuild that restarts hive-forge
and hive-c0re together races: every ensure_* fired at a refused socket and
left a stale 'provisioning failed' banner that never cleared, since the
pass is one-shot. Poll GET /api/v1/version (unauthenticated) until it
answers, bounded at 1 minute, before provisioning; on timeout proceed
anyway so a genuinely-down forge still surfaces its real errors. Mirrors
the readiness-retry the gateway-nginx path already has.
2026-07-19 19:07:04 +02:00
iris
06bc7e31c0 fix(#2289): wire sync_agent failures to dashboard warning banner
sync_agent() now returns bool (false if any step fails). ensure_all()
collects the names of agents whose sync failed and raises a single
set_boot_warning with the aggregated list:

  forge: per-agent sync failed for: alice, bob (see journal for per-step
  detail)

The static_kind() leak is already used for per-org boot warnings in the
same file — the leak is bounded (one per hive-c0re boot, not per request)
so reusing it here is appropriate.

The rebuild call site in job_queue/exec.rs discards the bool return and
keeps its existing tracing::warn! lines, which is the right separation:
rebuilds are their own retry loop and don't need to post a persistent boot
warning.
2026-07-19 14:57:21 +02:00
iris
f378f842ea forge: wire ensure_all() boot-warn sites to the dashboard banner
Adds set_boot_warning() to stats/warnings.rs: a one-shot variant of
set_warning() for steps that run once at hive-c0re boot (no periodic
retry to observe a later success and clear the banner via WarningGuard
drop). It forgets the guard, leaking it for the life of the process —
the banner clears on the next hive-c0re restart, which is also when a
config/environment fix would take effect.

Wires every remaining tracing::warn!()-and-forget site in
forge::ensure_all() to also raise a boot warning: core user/token
provisioning, per-org seeding, the operators-team grant per org, the
core/meta repo, shared docs repo, knowledge repo (+ its local clone),
both avatar uploads, and the final nixos-container list failure.

Split the core_token.is_some() body of ensure_all() into a new
ensure_all_orgs_and_repos() helper to stay under clippy's
function-length limit.

Deliberately out of scope: sync_agent() (shared with rebuild_agent(),
different blast radius) and ci_runner::ensure_ci_runner_registered()
(its own retry logic) — matrix::ensure_all() got the same slice-by-
slice treatment.
2026-07-17 13:15:47 +02:00
damocles
d124dd205a feat(#2554): add hivectl forge reconcile-config to reconcile local applied config against forge main 2026-07-17 12:55:42 +02:00
atlas
2941a889f0 feat(#2415): c0re ci_runner orchestration + wire into ensure_all
Part B (c0re half). New forge/ci_runner.rs: validate the hive-ci runner's
persisted .runner against the forge (GET /admin/runners/{id}); if absent or
stale, mint a fresh registration token (GET /admin/runners/registration-token,
raw request against the local http forge — forgejo-api 0.11 doesn't wrap it)
and hand it to hive-priv's RegisterCiRunner to write the host env-file +
restart the runner. Gated on HYPERHIVE_FORGE_CI_ENABLED; best-effort (never
aborts the startup sweep). Called from ensure_all after the org/repo seeding.
The nix boot-path change (drop prefetch gating, add runner precond, set the
env var) lands next on this branch.
2026-07-16 12:48:23 +02:00
damocles
673aea4e50 fold hive-c0re module tree into the daemon binary + drop dead pub items surfaced by bin-only (#2513) 2026-07-16 10:43:08 +02:00
damocles
1961afd9cc feat(#2471): post failing config deploy build log to the config pr 2026-07-15 18:16:12 +02:00
damocles
6e0f6893cf fix(#2393): ff-merge config prs via forge api instead of pushing main 2026-07-13 11:18:43 +02:00
atlas
7b1b1d9db8 fix(#2164): address argus review — Option<String> secret + stale hook cleanup
Two issues flagged by argus in PR #2388 review:

1. Empty-key fallback: when load_or_generate() failed, webhook_secret was
   String::new(). An attacker knowing this could forge deliveries with a
   valid HMAC of the empty key. Fix: change to Option<String>; on None,
   skip hook registration entirely and return 503 from /webhook/* handlers
   (rather than 401 with a misleadingly-verifiable empty-key HMAC).

2. Stale hook cleanup: on upgrade from old code, old loopback hooks
   (http://127.0.0.1:.../webhook/knowledge, .../webhook/config-pr) were
   left alongside the new domain-URL hook. Fix: during ensure_webhook /
   ensure_config_pr_webhook, after listing hooks, delete any that end with
   our path suffix but point at a different base URL.

clippy + nix fmt clean.
2026-07-12 02:21:33 +02:00
atlas
79a29873e3 fix(#2164): domain-URL webhooks + HMAC + config-PR polling fallback
Both webhook registrations (knowledge push + config-PR pull_request) now
use the public hive domain instead of loopback:
  https://<HYPERHIVE_HIVE_DOMAIN>/webhook/{knowledge,config-pr}

This routes deliveries through the gateway, bypassing the Forgejo SSRF
guard that blocked loopback delivery and silently broke the config-PR
merge flow since launch.

Changes:
- webhook_secret: new module — auto-generate + persist a 32-byte HMAC
  secret to STATE_ROOT/webhook-secret on first startup; verify
  X-Hub-Signature-256 on every incoming webhook POST (HMAC-SHA256).
- forge/mod.rs: ensure_config_pr_webhook now takes hive_domain +
  webhook_secret; sets secret in Forgejo hook config.
- workers/knowledge.rs: ensure_webhook same update.
- dashboard/webhook.rs: both handlers read raw Bytes first, verify HMAC,
  then parse JSON. Returns 401 on signature mismatch.
- dashboard/mod.rs: AppState carries webhook_secret; serve() takes it.
- main.rs: load/generate secret at startup; pass to registration tasks
  + dashboard; add 5-minute config-PR polling fallback task.
- forge/config_pr_poll.rs: new — scan agent-configs/* for open PRs with
  no pending MergeConfigPr approval; queue them. Idempotent.
- stores/approvals.rs: has_pending_merge_config_pr() for poll dedup.
- nix/modules/hive-gateway.nix: remove dashboardAuth from /webhook/
  location (HMAC replaces basic auth for webhook endpoints; Forgejo
  cannot send HTTP Basic credentials with webhook deliveries).
2026-07-11 23:28:16 +02:00
atlas
18e7c406b0 fix(#2377): doc_markdown + too_many_lines clippy lints
- Backtick-quote `pull_request` in doc comments (4x doc_markdown)
- Add #[allow(clippy::too_many_lines)] to server::dispatch (101/100;
  +1 line from submit_kind fetched_sha param in 5dd0a36f)
2026-07-11 12:19:52 +02:00
atlas
5e1863d231 fix(#2377): org_list_hooks returns Vec<Hook>, use .send() not .all()
org_list_hooks response type is Vec<Hook> (no pagination headers),
so .all() (which is impl'd for (H, Vec<T>) paginated responses) does
not compile. Switch to .send() — the non-paginated call path.

repo_list_hooks (used in workers/knowledge.rs) returns (H, Vec<T>)
and correctly uses .all(); the org variant is different.
2026-07-11 12:19:52 +02:00
atlas
c4d2391455 fix(review): drop libnull.rlib artifact + add Errors doc to ensure_config_pr_webhook
- Remove libnull.rlib accidentally committed in previous push (cargo
  check --no-default-features side-effect); add *.rlib to .gitignore
  to prevent recurrence
- Add # Errors section to ensure_config_pr_webhook doc comment
  (argus: missing on pub async fn returning Result<()>)
2026-07-11 12:19:52 +02:00
atlas
d5a81f9195 feat(#2377): forge-webhook-triggered config-PR merge flow
Replace the request_merge_config_pr MCP tool with a Forgejo
pull_request webhook on the agent-configs org. Agents now open a
config PR normally; hive-c0re auto-queues the MergeConfigPr approval
from the webhook event — no extra tool call needed.

Changes:
- dashboard/webhook.rs: add POST /webhook/config-pr handler
  - parses Forgejo pull_request payload (opened/synchronize)
  - strips agent-configs/<agent> prefix to extract agent name
  - calls submit_merge_config_pr → queues dashboard approval card
  - always 200 to prevent Forgejo retries; errors logged at warn
- dashboard/mod.rs: wire /webhook/config-pr route
- forge/mod.rs: add ensure_config_pr_webhook() — idempotent org-level
  hook registration on agent-configs at startup; CONFIG_ORG now
  pub(crate) for webhook handler
- main.rs: call ensure_config_pr_webhook alongside knowledge webhook
- socket_server/config_approvals.rs: drop handle_request_merge_config_pr;
  make submit_merge_config_pr pub(crate) for webhook handler
- socket_server/mod.rs: re-export submit_merge_config_pr; drop dispatch arm
- hive-sh4re/src/lib.rs: remove AgentRequest::RequestMergeConfigPr
  wire type; drop from ToolGroup::Approvals tool list
- hive-ag3nt/src/mcp/: drop request_merge_config_pr tool + args struct
- docs: update approvals.md (webhook trigger), conventions.md (tool
  group), agent-hierarchy.md, tools/lifecycle.md

Hardening from #2375-merge-config-pr-hardening branch preserved:
- pr_is_open check at queue time (rejects closed/merged PRs)
- atomic fetched_sha INSERT via submit_kind(fetched_sha: Some(&sha))

Approve-handler machinery unchanged (run_merge_config_pr,
ff_push_to_main, fetch_pr_head_into_applied, mark_pr_merged).
2026-07-11 12:19:52 +02:00
atlas
96eda4ed6b fix(#2375): pr_is_open state check at submission + atomic fetched_sha INSERT
Two hardening items from argus's review of #2374:

1. PR state check at submission:
   - Add `pr_is_open(repo, pr)` to forge/pr_merge.rs using
     `repo_get_pull_request` + `StateType` — early error if the PR is
     already closed or merged instead of queuing a card that fails later
   - Call it in `submit_merge_config_pr` before fetching the head sha

2. Atomic fetched_sha INSERT:
   - Add `fetched_sha: Option<&str>` to `Approvals::submit_kind` so
     the sha can be included in the INSERT rather than a follow-up UPDATE
   - MergeConfigPr already knows the sha before inserting the row
     (pr_head_sha runs first) → pass `Some(&sha)`, drop the separate
     `set_fetched_sha` call → truly atomic
   - ApplyCommit still needs two writes (sha resolved by git_fetch_to_tag
     after the row exists) → pass `None`, `set_fetched_sha` unchanged
   - All other callers (InitConfig, Spawn, UpdateMetaInputs,
     SchedulePrompt) pass `None` — no behavioural change
   - Add `fetched_sha_in_insert_is_readable_via_get` test covering the
     MergeConfigPr path
2026-07-11 12:19:52 +02:00
atlas
031edbd41f fix(#1868): use HIVE_FORGE_URL for internal forge calls
Replace the hardcoded FORGE_HTTP const with forge_http_base() which
reads HIVE_FORGE_URL from the environment (already set unconditionally
by hive-c0re.nix to http://<forge.domain>). Add forge_git_url() helper
that inserts core:<token> credentials between scheme and authority for
git push/clone URLs.

All call sites updated:
- forge/mod.rs: api() OnceLock + new forge_git_url/forge_http_base fns
- forge/repos.rs: push_meta, push_config, ensure_meta_remote
- forge/pr_merge.rs: tokenised_repo_url delegate + test loosened
- workers/knowledge.rs: clone + push URLs
- socket_server/mod.rs: clone_url in RepoCreated response

No new env var: HIVE_FORGE_URL was already the right knob (mara).

Closes #1868. Closes #2174 (this supersedes the operators-team fix from
the closed #2218, which is re-applied in the ensure_operators_team call
that was already merged separately).
2026-07-09 00:45:13 +02:00
müde
b8a3927c43 refactor(hive-c0re): port forge module + knowledge hooks to forgejo-api 2026-07-07 09:11:15 +02:00
müde
a17015f01e refactor(hive-c0re): split forge into submodules
mod.rs keeps the shared admin/http helpers + ensure_all/sync_agent;
user/token provisioning, repo/org/mirror ops, and the trust-boundary
PR-merge primitives move to users.rs / repos.rs / pr_merge.rs
2026-07-06 22:08:22 +02:00
müde
d190420946 refactor(hive-c0re): shared additive-migration helper in db
db::apply_migrations runs ALTER lists and ignores duplicate-column
errors (turn_stats' pattern); approvals, operator_questions, broker
reminders, and scheduled_prompts drop their hand-rolled
pragma_table_info guards. broker's acked_at migration stays bespoke —
its backfill must only run when the column was just created
2026-07-06 21:53:48 +02:00
Renamed from hive-c0re/src/forge.rs (Browse further)