Commit graph

1,927 commits

Author SHA1 Message Date
atlas
e7a4b33c1e nix(forge): drop tracker tag from ROOT_URL comment
Rewrite the ROOT_URL scheme comment as prose without the parenthetical
issue reference, which the tracker-tag lint flags in source files.
2026-06-17 18:32:21 +02:00
atlas
147fb46cdc nix(forge): derive ROOT_URL scheme from gateway TLS, not always http
Fixes #1724. The forgejo ROOT_URL was hardcoded `http://` even when the
gateway terminates TLS, producing broken clone links and mixed-content
redirects for a TLS deployment.

Derive the scheme + port from gateway state: `https` (on httpsPort) when
the gateway terminates TLS (`gateway.selfSignedTls` or `gateway.tls.certDir`
set), `http` (on gateway.port) otherwise — dropping the port suffix on the
canonical port for the scheme (80 / 443). The per-agent in-cluster
HIVE_FORGE_URL is unaffected (it stays http on the gateway's :80 path);
this only changes the public ROOT_URL forgejo advertises.

Refreshes the `rootUrl` option description (the TLS scheme is auto-derived
now, so the manual-override-for-TLS note is gone).

Eval-proven: gateway.selfSignedTls = true → ROOT_URL =
https://forge.<domain>/; false → http://forge.<domain>/.
2026-06-17 18:32:21 +02:00
damocles
8e24814efe feat(dashboard): batch POST /api/permissions for save-all perms (#1719) 2026-06-17 18:21:56 +02:00
atlas
75dbcc2aa2 nix(c0re): trim the network-isolation narrative from the forge/matrix URL comments
Per mara's review of #1718: the HIVE_FORGE_URL / HIVE_MATRIX_URL comments
carried "network isolation is now unconditional / shared-netns removed"
backstory that doesn't belong on those keys. Trimmed to describe just the
value (the gateway vhost) + the gatewayHost null-guard. The
isolation-removal narrative stays where it's on-topic — the network
module's own deprecated-option descriptions and warning text.
2026-06-17 16:58:20 +02:00
atlas
e8c2ef087c nix(c0re): set HIVE_FORGE_URL to the gateway URL directly, drop dead if/else
Per mara's review of #1718: this env block only exists when hyperhive is
enabled (it lives inside `config = mkIf cfg.enable`), so the
`if hyperhive.enable then gateway else loopback` branch was dead — the
loopback fallback could never be reached. Set HIVE_FORGE_URL to the
gateway vhost directly.

HIVE_MATRIX_URL drops the same redundant `hyperhive.enable` term but
keeps the gatewayHost null-guard — that one is a real fallback for a
domain-less config, not the dead enable branch.
2026-06-17 16:56:26 +02:00
atlas
61db5afc22 nix(c0re): guard agent forge/matrix URLs on hyperhive.enable, not network.enable
Addresses argus's review of #1718: the HIVE_FORGE_URL / HIVE_MATRIX_URL
conditionals still keyed off the now-deprecated `network.enable`, so
setting it to `false` would point agents at host loopback (unreachable
from their private netns) even though isolation stays on — contradicting
the "deprecated and ignored" warning. Guard on the top-level
`hyperhive.enable` instead, so `network.enable` truly has no effect
anywhere and the loopback branch only covers a hyperhive-disabled host.

Eval-proven: with `network.enable = false`, HIVE_FORGE_URL now resolves
to the gateway vhost (`http://forge.<domain>`), and the deprecation
warning still fires.
2026-06-17 16:07:39 +02:00
atlas
78482cb624 nix: remove the non-isolated (shared-netns) mode
Network isolation is validated working end to end (forge + matrix reach
the gateway under private netns), so the shared-host-netns mode is
removed — there is now one mode.

- hive-network.nix: the bridge/DNS block and the isolation overlay are
  now unconditional whenever hyperhive is enabled (gated on
  services.hyperhive.enable, not the per-feature toggles). The redundant
  isolateContainers-implies-enable guard is dropped.
- network.enable and isolateContainers are kept as DEPRECATED no-op
  options so existing configs that set them to true still evaluate
  cleanly; setting either to false now warns (gated on hyperhive being
  enabled, so a non-hyperhive host gets no spurious warning) and has no
  effect. Both are slated for removal in a future release.
- hive-c0re.nix: the HIVE_FORGE_URL / HIVE_MATRIX_URL conditionals drop
  the "&& isolateContainers" clause — agents always reach forge/matrix
  through the gateway vhost now.
- hive-matrix.nix: refresh one stale comment.

The Rust lifecycle is env-driven (HIVE_NETWORK_ISOLATION), so no Rust
change is needed: the env is now always set, so the existing
private-netns path always runs.

Supersedes the default-on flip (the option it would have defaulted is
gone).
2026-06-17 15:29:52 +02:00
damocles
8acf9dcd9b hive-forge: surface pr-reviews write flags in its --help summary (closes #1716) 2026-06-17 15:16:58 +02:00
damocles
726561f66c harness: assert matrixAccounts tokenFile basenames start with matrix-token (#1702 BE-3) 2026-06-17 15:13:17 +02:00
damocles
17e6afef8d ci: re-trigger (prior run hit a runner timeout, not a code error) 2026-06-17 13:49:45 +02:00
damocles
f723806eae list: reject --page 0 and --limit 0 (1-based; clap range parser, per review) 2026-06-17 13:49:45 +02:00
damocles
2005b205e9 list: paging via --page instead of --all (token-bounded per call, per review) 2026-06-17 13:49:45 +02:00
damocles
0aef643797 hive-forge: add --all to list for full-pagination repo-wide sweeps 2026-06-17 13:49:45 +02:00
damocles
21e5233c1f ci-log: graceful message when a run's logs are gc'd (404/500) instead of raw http error 2026-06-17 10:05:21 +02:00
damocles
e8d39ecb8b hive-sh4re: make harness_dir the single resolver; hive-ag3nt delegates (#1450) 2026-06-16 12:36:48 +02:00
damocles
a69844d463 hive-forge: fix ci-log clippy cast_possible_truncation (use try_from) 2026-06-16 11:51:01 +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
damocles
1d259e84f2 dashboard: tighten is_plain_ident to match hive-priv validate_name_chars (argus review) 2026-06-16 11:18:18 +02:00
damocles
9c480daf0a dashboard: POST /matrix-account-login + account-aware hive-priv matrix-token write (BE-2) 2026-06-16 11:18:18 +02:00
damocles
8799a9aa11 hive-matrix-mcp: document redaction power-level + concurrency semantics 2026-06-16 11:06:39 +02:00
damocles
55ba30aa18 hive-matrix-mcp: cover send_redact reason=None in round-trip test (argus note) 2026-06-16 11:03:53 +02:00
damocles
1c0f7a72f7 hive-matrix-mcp: add send_redact tool to delete matrix events 2026-06-16 10:58:34 +02:00
damocles
79af902d47 dashboard: drop tracker tag from matrix_accounts doc comment (lint) 2026-06-16 10:29:17 +02:00
damocles
1ebc384e66 dashboard: GET /api/matrix-accounts — list an agent's provisioned matrix accounts (#1698) 2026-06-16 10:29:17 +02:00
iris
06d57a61f8 dashboard: fix matrix-accounts GET path to /api/matrix-accounts
BE-1 (the list endpoint) lives at GET /api/matrix-accounts (reads go under
/api/, matching /api/state and /api/operator-inbox); the page was calling
the un-prefixed /matrix-accounts. Correct the fetch + the contract comment +
the docs reference. POST /matrix-account-login (mutation, root path like
/approve) is unchanged.
2026-06-16 10:22:53 +02:00
iris
8bb3bd82a1 dashboard: drop matrix-accounts experimental notice; relabel list as provisioned
Per-account token-failure isolation is now live on the matrix daemon, so a
bad credential entered via this form can no longer take down the agent's
whole matrix session — remove the experimental notice (+ its now-unused CSS).

Also relabel the account list 'configured accounts' -> 'provisioned accounts'
to match the v1 read path (lists accounts with a stored token; a
config-declared-but-unprovisioned account appears once provisioned through
the form). Docs + module header updated to match.
2026-06-16 10:22:53 +02:00
iris
9293fe3ac9 dashboard: matrix-accounts page — provision/login per-agent external matrix accounts
New standalone H0M3 page (/matrix-accounts.html) and tile. An agent
picker drives a list of that agent's configured matrix accounts (name,
homeserver, token-stored status) and a provision form that logs in by
password or stores an existing token.

Frontend half of the per-agent external matrix-account provisioning
work. Built against the v1 backend contract:
  GET  /matrix-accounts?agent=<name>
       -> { accounts: [ { name, homeserver, token_present } ] }
  POST /matrix-account-login  (x-www-form-urlencoded, operator-auth)
       fields: agent, account, homeserver, mode=password|token,
               user_id?, password?, token?
       -> 2xx { ok, user_id } | 4xx { error }

The token is never echoed back; secret inputs are cleared on submit.
Token-status dot reflects token-stored, not live session (a true
up/down indicator needs the daemon account registry, a follow-up). The
form carries an experimental notice pending per-account failure
isolation on the matrix daemon.

Blocked from merge on the backend endpoints and the daemon
failure-isolation fix; opening for review + to pin the UI/backend wire
contract.
2026-06-16 10:22:53 +02:00
iris
c187366961 dashboard: dedent call.js moved blocks to module top level
Cosmetic follow-up to the Y3R C4LL extraction (argus nit on #1705): the
approvals + questions sections kept their original 2-space IIFE indentation
from tabs.js, which read as if they were inside a block and was inconsistent
with the unindented operator-inbox section + the other domain modules.
Uniform dedent — no logic change, build unaffected.
2026-06-16 10:22:53 +02:00
iris
a0fc7e1a9f dashboard: extract questions domain into call.js — Y3R C4LL split complete
Final domain: move the operator question queue (questionRowCache, the
sync/apply/filter/fingerprint/buildQuestionLi/renderQuestions fns, formatTtl,
and the .q-ttl countdown ticker) from tabs.js into call.js. tabs.js imports
syncQuestionsFromSnapshot, applyQuestionAdded, applyQuestionResolved,
renderQuestions, and the activeQuestionCount getter; refreshTabCounts now
sums activeApprovalCount()+activeQuestionCount()+operatorInboxCount().

The .status-age (SW4RM) and .approval-ts/.reminder-due/.sched-due
(approvals+schedules) tickers stay in tabs.js — only the questions-specific
.q-ttl ticker moved. questionsState stays in state.js (shared with the SW4RM
badges). Behaviour-preserving; no visual change.

Net: tabs.js 2323 to 1603 lines; the Y3R C4LL pane now lives in call.js,
alongside permissions.js / schedules.js as a per-domain module.
2026-06-16 10:22:53 +02:00
iris
d0ec0d2896 dashboard: extract operator-inbox + approvals into call.js (#1451)
Begin splitting the tabs.js monolith: lift the Y3R C4LL domain into a new
call.js module (mirrors permissions.js / schedules.js).

- questionsState + QUESTION_HISTORY_LIMIT move to state.js: they're read by
  both the SW4RM container rows (per-agent question-count badges) and the
  Y3R C4LL questions domain, so they're cross-domain shared state.
- operator-inbox domain (state + refresh/render/mark/append) → call.js.
- approvals domain (state + sync/apply/render/diff-panel/history) → call.js.
- call.js exposes count getters (activeApprovalCount, operatorInboxCount);
  the entry's refreshTabCounts pulls them. Live-mutation paths call an
  injected onCountsChanged callback (registered via initCall at boot)
  instead of reaching into the coordinator (avoids a circular import).
- renderPeerHives, physically interleaved in the moved range but part of
  the SW4RM/peers domain, stays in tabs.js.

Behaviour-preserving; no visual change. Questions domain follows next.

(Recreated after a harness-restart git-object corruption; identical content.)
2026-06-16 10:22:53 +02:00
atlas
e6f9679ec9 hive-forge: add issues/issue-list discovery aliases for the list verb
The verb that lists issues/PRs with filters is `list`, but `issues` and
`issue-list` are the names people reach for first — and clap's
"did you mean" tip points at `issue` / `issue-create`, never at `list`.
Two agents independently failed to find it that way.

Add `issues` and `issue-list` as visible clap aliases so the verb is
discoverable under the names people actually type (and they show in
`--help`). No behaviour change; `list` stays canonical.
2026-06-16 00:18:32 +02:00
damocles
e5cd4e58f1 matrix multi-account: isolate per-account stale-token failure so a bad secondary token doesn't kill the daemon 2026-06-15 23:13:45 +02:00
atlas
c27f915e47 fix(meta): forward HIVE_FORGE_URL etc. into globalEnvironment
The forwarded host env vars (HIVE_FORGE_URL, HIVE_FORGE_PUBLIC_URL,
HYPERHIVE_HIVE_DOMAIN, ...) were emitted only into the harness
service's own unit environment. Under network isolation that left the
bash-task runner, the matrix daemon, tea-login and interactive shells
without HIVE_FORGE_URL, so `hive-forge` and friends fell back to the
loopback default (localhost:3000) — unreachable from a private netns,
where the in-cluster gateway URL (forge.<domain>) is the only path.

Emit the forwarded set into systemd.globalEnvironment too, which every
unit + shell in the container inherits, so all in-container surfaces
see the same forge/matrix endpoints. The existing harness-service
forward is kept (harmless redundancy). Shared netns is unaffected (the
localhost default still works there).

Adds a regression test asserting the forwarded vars land inside the
globalEnvironment block, and the generated flake parses.
2026-06-15 22:04:43 +02:00
damocles
f7199f183c drop issue number from source comment (tracker-tag lint) 2026-06-15 21:55:55 +02:00
damocles
354b270d2a matrixAccounts url: single-source-of-truth default binding (addresses review) 2026-06-15 21:48:51 +02:00
damocles
d15ecff6d2 isolation: forward HIVE_MATRIX_URL so matrix daemon reaches the gateway 2026-06-15 21:38:34 +02:00
lexis
314fb8298f docs: add agent.nix hyperhive.matrixAccounts multi-account matrix configuration (follow-up to #1692) 2026-06-15 21:28:36 +02:00
damocles
8ba0abcf27 matrixAccounts: assert no extra named 'main' (reserved for hive account) 2026-06-15 21:24:16 +02:00
damocles
36fb041c55 matrixAccounts: hive 'main' is implicit primary, option declares extras only 2026-06-15 21:24:16 +02:00
damocles
63db108516 rework matrixAccounts to attrset keyed by name + matrixPrimaryAccount 2026-06-15 21:24:16 +02:00
damocles
dcc059b4b8 feat: hyperhive.matrixAccounts nix option (matrix multi-account layer 2) 2026-06-15 21:24:16 +02:00
atlas
26d81cd36e fix(harness): stop resolvconf/dhcpcd clobbering the bridge DNS resolver
Under network isolation, agents lost DNS resolution of the hive's own
zones (forge.<domain>, matrix.<domain>): the hyperhive-isolated-dns
oneshot points resolv.conf at the bridge resolver, but resolvconf
regenerated it from host-tracking right after (dhcpcd re-triggering that
when the veth comes up), reverting to the non-authoritative host
resolver. Agents then couldn't reach the in-hive forge or matrix through
the gateway, which broke an isolate-by-default rollout.

Take resolvconf and dhcpcd out of the resolv.conf loop: disable
resolvconf and tell dhcpcd not to touch resolv.conf (without disabling
dhcpcd itself, so the veth still gets its address). Then the last writer
owns it -- the nixos-container host-copy in shared netns, or the oneshot
in isolated mode. Mirrors the approach the matrix container already
takes. Eval-checked; flake check passes.
2026-06-15 20:45:20 +02:00
damocles
3b26f58a00 docs: note ping arm in dispatch_op is unreachable 2026-06-15 20:28:31 +02:00
damocles
8e79eb4f26 feat: multi-account matrix daemon (account-routed mcp surface) 2026-06-15 20:23:42 +02:00
damocles
73c53c7d4a feat(#1685): surface attachments in read_room + add download_file 2026-06-15 18:17:35 +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
damocles
801b886a4c drop stale send_file_dm doc refs (per review) 2026-06-15 17:59:02 +02:00
damocles
07941421c1 replace send_file_dm with open_dm resolver (per review) 2026-06-15 17:59:02 +02:00
damocles
19f1fa4f28 rustfmt send_file handlers 2026-06-15 17:59:02 +02:00
damocles
cbbd6f3d6e feat(#1681): add matrix send_file + send_file_dm tools 2026-06-15 17:59:02 +02:00