Watch
0
0
Fork
You've already forked hyperhive
0
Commit graph hyperhive/hive-forge/src/client.rs
Author SHA1 Message Date
atlas
ccc6993703 hive-forge: exit after HIVE_FORGE_DEADLINE instead of hanging on a stalled forge
forgejo-api 0.11.1's sync client builds its own reqwest client with no
timeout and takes no caller-supplied client (sync.rs: new and
with_user_agent are the only constructors), so a forge that accepts a
connection and then stalls blocked every typed verb forever.

A watchdog thread now starts before the client is built. After
HIVE_FORGE_DEADLINE seconds (default 900, five minutes above
DOWNLOAD_TIMEOUT) it prints a hive-forge: FAILED: line and exits 75.
It bounds the whole command, not each request, and a write may already
have landed when it fires; the help text and docs/tools/forge.md say so.
2026-10-17 08:01:23 +02:00
atlas
efbfec6d01 config PRs: remove the hive's config-PR webhook, poll and core merge
An operator's merge on the forge deploys a config PR through
swarm-controller's DeployRequest{rev}. The hive-side path that queued a
MergeConfigPr approval and merged the PR as `core` goes:

- the `/webhook/config-pr` receiver, its HMAC secret, the WebhookRegister
  boot node and the org-hook registration; the hive vhost's `/webhook/`
  location
- the 5-minute config-PR poll
- ApprovalKind::MergeConfigPr, its dashboard card, and the deploy DAG it
  drove (DeployWindow, MergeVerify, DeployApply, FinalizeDeploy,
  DeployTail), with verify_commit, the two-phase meta deploy, rollback
  refs, the PR-failure comment and forge/pr_merge.rs
- `fetched_sha`, `sha_short`/`pr_number` on approval events, and
  `sha`/`tag` on HelperEvent::ApprovalResolved: only the merge path set
  them

`config_repo`, `merged_pr_for_commit` and `post_pr_comment` move to
forge/pr_comment.rs for the merged-rev deploy's refusal comment.
Approvals v5 drops stored `merge_config_pr` rows; a test reopens a v4
database holding them.

Closes #4850
2026-10-02 23:13:03 +02:00
atlas
2c7e586f47 forge: external forge accounts live in swarm bao; the agent fetches them itself
An operator now links an agent's external forge account (label, base URL,
token) in the swarm UI. swarm-controller stores it at
swarm/agents/<agent>/forge/<label>. There is no index: the store's
listing of the agent's forge/ directory is the set of accounts.

In the agent, hive-agent-forge-accounts (oneshot + 2-minute timer, as
the agent user, under its own store certificate) lists
swarm/agents/<agent>/forge/ with the `list` #4866 grants an agent on its
own metadata subtree, reads each account, and writes
<state>/forge-<label>-token and forge-<label>.json in the names and shape
hive-forge -f already reads. An empty listing (a 404, which `bao kv list
-format=json` answers with `{}` and an empty stderr) is zero accounts; a
denial or an unreachable store fails the unit. It never deletes: files
for labels not listed, including ones the hive wrote, stay as they are.

Removed: the dashboard FORGES tab (credentials.js/html section and its
CSS), hive-c0re's extra_forges.rs and its routes, priv_client's
extra-forge calls, and hive-priv's WriteAgentExtraForgeAccount /
DeleteAgentExtraForgeAccount with their helpers. The GITHUB tab and
WriteAgentGithubToken stay.

Also: persistence.md's matrix avatar note names the exit-75 restart on a
changed account listing, not the dashboard, as what brings a linked
account up.

Refs #4348
2026-10-01 18:05:33 +02:00
atlas
535073011b hive-forge: bound the web-router client's connect and request waits
The raw reqwest client hive-forge uses for Forgejo's web-router-only
routes (attachment downloads, Actions artifact/log routes) had no
timeout, so a hung Forgejo response blocked the calling CLI invocation
indefinitely.

Adds a 5s connect_timeout (matching #4737's outbound-HTTP sites) plus
a per-call request timeout: 15s (config_pr_poll's forge budget) for
the small JSON calls (get_api_json, post_json_web), and 10 minutes for
get_bytes_named/get_bytes_raw, which download attachments, Actions
artifact zips and persisted job logs that can be large.
reqwest::blocking has no separate read/idle timeout, so a single
whole-request budget has to cover those downloads; the smaller JSON
budget would cut them off partway through.

Refs #4746, #4737
2026-09-29 09:17:47 +02:00
atlas
dd32a395f7 agents: pull the forge token from bao; drop tea-login
forge-token.nix fetches swarm/agents/<agent>/forge-token under the
agent's own store identity into /run/hive-agent-forge-token/token, and
re-fetches on a timer so a rotation lands. hive-forge, the git
credential helper, hive-forge-notify, forge-avatar-sync and the web UI
read that file first and fall back to <state>/forge-token.

tea-login is deleted: it copied the token into ~/.config/tea, which
docs/swarm/credentials.md forbids for a store secret. hive-forge covers
the same verbs. swarmctl gains agent mint-forge-token.

Refs #3782
2026-09-24 17:48:53 +02:00
damocles
9065898e08 hive-forge: carry the HTTP status structurally instead of flattening it to a string 2026-09-03 01:34:02 +02:00
atlas
cdac6091eb hive-forge: name the CI job ci-log actually served
`ci-log --run <n> --job <idx>` accepted any index and exited 0. Past the
run's job count it printed job 0's log, with nothing marking the
substitution. Measured against run 3363 (4 jobs): indices 0-3 gave three
distinct md5s, while 99 and 12345 both returned output byte-identical to
job 0.

Both web log routes clamp an out-of-range index to job 0 and answer 200.
I had claimed nothing in the response distinguishes them -- that was
measured on the body only, and the body was the wrong place to look. The
`Content-Disposition` header names the job the server actually served:

    --job 3   -> filename="ci-doc-pointer lint-8363.log"
    --job 99  -> filename="ci-nix flake check-8360.log"   (job 0's)

So the fix is to report what came back rather than to pre-validate the
index. Establishing the real job count needs two extra API calls on every
indexed read -- `ActionRun` carries no job count, and the only job
endpoints are the per-run listing and a per-job log keyed by internal job
id, not by index. The header costs nothing: it is already in the response
being read.

The provenance line goes to stderr, so it cannot corrupt a piped log;
under `--json` it is a `served` field instead. An out-of-range `--job` is
therefore no longer an error -- it is a read whose true subject is named.

`get_bytes_named` is a sibling of `get_bytes_raw` rather than a signature
change, leaving the attachment and artifact download paths untouched.

Rebased onto main after the two flat-rename PRs landed; the only conflict
was the test module's import list, resolved by keeping both sides. While
reading the surrounding context this commit's own defect surfaced:
`check_status`'s doc comment had been left glued to the head of
`disposition_filename`'s, so one function carried two unrelated
descriptions and the other carried none. No gate can see that -- it is
well-formed rustdoc either way.
2026-09-02 21:57:43 +02:00
atlas
74b48c5afa hive-forge: test the two invariants dependency.rs documents but never checked
dep_meta's comment records a measured 404: leaving owner/repo as None
made every same-repo dependency fail, because forgejo resolves the
dependency's repo from the request BODY rather than the URL. Nothing
stopped a later tidy-up from simplifying them back to None.

index() promises "error instead of wrapping". A wrap would not fail
loudly -- it would hand the forge a negative index it looks up as some
other issue.
2026-09-02 19:01:31 +02:00
damocles
fe7bf81d4a hive-forge: defer repo resolution to the accessor, not construction 2026-09-02 17:08:01 +02:00
damocles
676f7715fd hive-forge: stop embedding the forge token in clone URLs 2026-08-26 00:20:58 +02:00
damocles
ed305bbe00 hive-forge: shell out to git for origin-remote inference instead of hand-parsing config 2026-07-29 18:44:28 +02:00
damocles
f034ffb9d8 hive-forge: infer active repo from cwd's git remote, demote HIVE_FORGE_REPO 2026-07-29 18:37:37 +02:00
atlas
b670291347 fix(#2565): lenient notifications deserialize in hive-forge read-guard 2026-07-17 16:45:56 +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
müde
4636987469 refactor(hive-forge): port CLI verbs to forgejo-api 2026-07-07 09:24:53 +02:00
atlas
c9c59c2a1d hive-forge: ci-rerun dispatches a fresh run via workflow-dispatch API
The previous implementation POSTed Forgejo's run-page rerun web route,
which is CSRF-gated and answers a bare token POST with 404 — so the verb
never actually re-ran anything against the agent token.

Rework it to dispatch a fresh run of the workflow via the
GitHub-compatible workflow-dispatch API
(POST /repos/<o>/<r>/actions/workflows/<workflow>/dispatches {"ref":<branch>}),
which accepts a plain agent token (verified end-to-end on Forgejo 15.0.3).
A dispatched run is equivalent to the old empty-commit retrigger, minus
the commit.

The branch is resolved from exactly one of --pr (the PR head branch),
--run (branch + workflow looked up from that run in the Actions runs
list), or --branch (directly); --workflow picks the workflow file for
--pr/--branch (default ci.yml). Dispatch re-runs the whole workflow, so
the old --job single-job variant is dropped.

Also add workflow_dispatch to .forgejo/workflows/ci.yml for explicitness
(Forgejo 15.0.3 dispatches the pull_request workflow without it, but the
trigger makes the API path intent-clear and cross-version robust), remove
the now-unused Client::post_web_no_content, and update docs/tools/forge.md.
2026-06-23 14:53:11 +02:00
atlas
cd4bdf4eea feat(#1778): add hive-forge ci-rerun to re-run CI without an empty commit
When a CI run fails for a transient reason (remote-builder flap, cold-daemon
window, act_runner hiccup) the only retrigger path was an empty commit, which
litters PR history and forces a force-push to clean up. This verb POSTs the
rerun action directly.

- `ci-rerun --run <n>` re-runs all jobs of a run (run number = the `runs/<n>`
  the UI shows, same value ci-log / artifact-get take, surfaced as a CI
  context's target_url by pr-status).
- `--pr <n>` resolves the run from the PR head sha's CI status target_url.
- `--job <i>` re-runs a single job.

Forgejo exposes no REST endpoint for rerunning a run, so this rides the run
page's web routes (`<base>/<owner>/<repo>/actions/runs/<n>[/jobs/<i>]/rerun`)
via a new `Client::post_web_no_content` (web base like post_json_web, tolerates
the redirect/empty response the rerun handler returns). Mirrors ci-log's
web-route approach + auth path.

docs/tools/forge.md updated with the verb.
2026-06-23 14:53:11 +02:00
atlas
156ca70b1e feat(#1888): read-before-comment guard via forge notification read-state
Rebuild on forge's own notification read-state instead of the local
seen-cursor mirror (operator-nacked: mirrors state forge owns + drifts
on restart).

- comment refuses to post when forge has an unread notification for the
  thread (someone commented since you last read it); --force overrides.
  Degrades open if the notification check itself fails.
- comments / view mark the thread's notification read (the "I've seen
  it" signal), clearing the guard for a subsequent reply.
- new crate::notify (no local file): unread_thread_id pages repo-scoped
  unread notifications (newest-first, capped) + subject-url number match;
  mark_thread_read via the new client.patch_no_content.

Pairs with the forge_notify harness change (#1895, merged) that leaves
delivered notifications unread until the agent actually reads. Covers
pr/issue comment too (they delegate to comment::run).
2026-06-22 18:54:40 +02:00
damocles
2223af1b21 hive-forge: add ci-log verb for actions job step logs (closes #1674) 2026-06-16 11:34:31 +02:00
atlas
6490fc422e feat(hive-forge): add artifact-get verb to download CI run artifacts
Forgejo 15 has no REST endpoint to download an Actions artifact — the
only path is the web UI download route, which is keyed by the run's
global id rather than the per-repo run number shown in run-page URLs.
The REST artifacts list route keys off the run number instead, so the
two can't be chained directly.

artifact-get takes the run number (what pr-status surfaces as a CI
context target_url), translates it to the global run id via the REST
runs list by matching each run's html_url tail, then GETs the web
download route with the agent's forge token. Saves the artifact zip to
a path (default /tmp/forge-artifact-<name>.zip) or streams to stdout
with -o -.

The artifact name is percent-encoded into the path. The encoder that
list already used for query-string filters is promoted to a shared
verbs::pct_encode helper so both call sites stay in sync.

Lets an agent pull a CI-built artifact (e.g. a paper PDF) into /shared
without host access.
2026-06-15 17:59:38 +02:00
atlas
9d8367bb24 feat(hive-forge): add pr-merge verb (#1670)
Adds `hive-forge pr-merge <n> [--method merge|rebase] [--keep-branch]
[--force]` wrapping POST /repos/{owner}/{repo}/pulls/{n}/merge, so agents on
the peer-review-and-merge workflow have a CLI path instead of the raw API.

- Methods: merge (default) | rebase. Squash is intentionally not offered.
- Deletes the head branch after merge unless --keep-branch.
- Safe by default: refuses unless the PR is mergeable, CI is not red, and no
  review's current verdict requests changes (latest-per-reviewer wins, so a
  later approval clears an earlier request-changes). --force overrides and
  also sets Forgejo's force_merge.
- New client helper post_no_content for the 200-empty-body merge response.
2026-06-15 11:36:17 +02:00
atlas
c7ffe9336b docs(hive-forge): add # Errors to put_no_content (argus review) 2026-06-14 22:09:31 +02:00
atlas
3790faf318 feat(hive-forge): add repo-create + repo-add-collaborator verbs 2026-06-14 22:09:31 +02:00
atlas
ab1b07acce ci(#1555): prose-ify legacy tracker tags; add lint:allow escape hatch
Clean the legacy backlog so the tracker-tag lint can become a required
gate (mara's warn-during-cleanup -> full-tree-enforcement path). Rewrite
the ~33 real `closes/see #NNN` provenance refs in doc-comments to prose
across hive-forge, hive-c0re, hive-ag3nt, hive-matrix-mcp, hive-sh4re,
and add a `lint:allow` line marker to check-issue-refs.sh for genuine
non-tracker `#<digits>` (a hash-digit heading-detection test input).
Tree is now lint-clean; tracker-tag lint ready to promote to required.
2026-06-10 13:14:42 +02:00
damocles
79101edc2f address argus: drop issue tags from clone/pr-create module docs, fix stale knowledge wording 2026-06-05 20:51:50 +02:00
damocles
c103ae5f10 feat(#1399): hive-forge clone verb + pr-create --agit for no-fork PRs 2026-06-05 20:51:50 +02:00
damocles
98f96e5435 feat(#1315): hive-forge attachment-get verb - download attachments by uuid or url 2026-06-05 00:46:49 +02:00
damocles
d3239fef35 fix(#1233): subscription --watch uses PUT, GET handles 404 as not-watching 2026-06-03 23:23:51 +02:00
atlas
4bff450343 feat(gateway): hivectl gateway user management + fix htpasswdFile assertion
Add `hivectl gateway {create-user,delete-user,list-users}` subcommands for
managing htpasswd files used by gateway Basic auth. Pure Rust bcrypt
(cost 12, $2y$ prefix nginx accepts). No external htpasswd binary required.

Also fix the NixOS module assertion: `cfg.auth ? htpasswdFile` is always
true in the module system (declared options always exist as keys); switch
to `nullOr path; default = null` + `!= null` check so the assertion
actually fires with a useful error when enable=true but no file is set.
Guard bind-mount and nginx config against null to prevent eval errors.

Update docs/gateway.md to show hivectl commands instead of raw htpasswd.
2026-06-01 23:25:28 +02:00
damocles
703018106a hive-forge: add lint verb for triage queries (closes #505) 2026-05-27 12:01:54 +02:00
damocles
d0a1ee037b hive-forge: make --json a global flag (closes #421) 2026-05-26 15:55:59 +02:00
damocles
15195b0c47 hive-forge: #[must_use] on Client::repo (mara@#407) 2026-05-25 02:16:53 +02:00
damocles
badf21714b hive-forge: global -r/--repo flag instead of per-verb [repo] positional (mara@#407) 2026-05-25 02:16:53 +02:00
damocles
595e3c040c hive-forge: rewrite bash CLI helper as a rust binary (closes #280) 2026-05-25 02:16:53 +02:00