Commit graph hyperhive/hive-c0re/src/forge/users.rs
Author SHA1 Message Date
atlas
20419ccd41 swarm-controller: own the swarm-wide forge objects; hive-c0re stops creating them
The orgs agent-configs/internal/agents (plus mirror owners), the
operators team in agents and agent-configs, the pull-mirrors,
internal/docs, internal/knowledge (public, README-seeded) and the
agent-configs org avatar are one set per forge. hive-c0re ensured them in
its boot sweep, as the core admin, and only on the hive co-located with
the forge container.

swarm-controller now reconciles them at start and every 5 minutes
(forge/objects.rs: observe -> pure plan -> apply). A failed object logs
a warn line plus a pass summary and is retried next tick. create_repo
ensures the agent-configs org and its operators team first, so a config
repo's merge gate never depends on the periodic pass having run.

hive-c0re drops ensure_org, SEEDED_ORGS, ensure_mirrors/ensure_mirror_repo,
ensure_operators_team, ensure_shared_docs_repo, ensure_knowledge_repo/
set_repo_public, seed_readme, ensure_config_org_avatar and the one-shot
knowledge::remove_webhook cleanup, with their now-unused helpers.

nix: the mirror list moves from the hive-c0re unit
(HYPERHIVE_FORGE_MIRRORS) to the swarm-controller unit
(SWARM_CONTROLLER_FORGE_MIRRORS), with an eval warning when mirrors are
declared on a host that runs no controller. c0re.orgAvatarPng is renamed
to deploy.swarm-controller.configOrgAvatarPng.

Refs #3782
2026-09-25 08:36:05 +02:00
atlas
ef494af188 hivectl: drop forge create-user; SSO makes a human's forge account
The forge now creates a human's account on their first authelia login,
so the verb has no job left. Deletes it, HostRequest::ForgeCreateUser,
its handler, provision_user_token, change_user_password and the hive's
TOKEN_SCOPES. change_user_password also passed the password as an
argument to `forgejo admin user change-password`, so it showed in the
container's process list.

ensure_user_exists and mint_token stay for the `core` bootstrap, their
one caller now. ensure_user_exists loses its password parameter: only the
deleted path set one.

Refs #3782
2026-09-25 08:29:56 +02:00
atlas
b5d07d4df2 hive-c0re: stop minting agent forge tokens
Delete ensure_user_for and mint_and_persist_agent_token, the user step
of sync_agent (the per-rebuild re-mint, #4644) and of
forge_after_first_spawn, and the hive-priv WriteAgentForgeToken request
that wrote the token into the agent's state dir. hivectl forge
create-user now refuses an agent and points at swarmctl agent
mint-forge-token. mint_token, ensure_user_exists and TOKEN_SCOPES stay:
provision_user_token and the core bootstrap still call them.

Refs #3782
2026-09-24 17:48:53 +02:00
atlas
2c066cc871 hive-c0re: pin the two security boundaries that had no test
`is_forbidden` and the webhook-secret load/regenerate path were the last two
entries on the shortlist in hyperhive/hyperhive#3950; the other three landed in
hyperhive/hyperhive#4650. Both are classification logic whose failure mode is
silence, which is why they are worth a test rather than a coverage line.

`is_forbidden` gates the arms that tell an operator a Forgejo admin PATCH was
refused for want of a scope, and which credential to delete and re-mint to fix
it. One of those PATCHes is `ensure_repo_creation_disabled` — the lockdown that
stops an agent creating a repo it owns and self-merging in it. Two tests: a 403
is recognised in both shapes the typed client produces (the spec-listed
`Forbidden` kind and the bare `UnexpectedStatusCode`), and nothing else is —
not a 401, whose remedy is the automatic re-mint one function down, and not a
transport error that never reached the forge at all.

`load_or_generate` grows the path-taking half `load_or_generate_at`, the same
seam `swarm-controller`'s `webhook::load_or_generate_at` already has and for
the same stated reason. Three tests over it: a valid stored secret is returned
verbatim and never rotated (the newline this module writes itself makes the
trim load-bearing, not defensive); a malformed one is replaced by a secret that
reaches *disk*, not just the caller, and is then stable; and each near miss —
empty, whitespace, 63 chars, 65 chars, right length with a non-hex char — is
refused. That last one is the security case: `Hmac::new_from_slice` accepts a
key of any length, empty included, so a relaxed check fails nowhere and just
keys every signature off a guessable value.

Every test was confirmed able to fail: six mutations of the code under test,
each watched red, then reverted. The two halves of the validity check and the
two arms of `is_forbidden` were broken separately, so neither test passes on
one arm alone.
2026-09-23 17:00:44 +02:00
iris
07b62612b0 docs: restructure into topic subdirectories, collapse duplicated index
Per mara's go-ahead on hyperhive#3902 ("getting started is good, but
terminal rendering does not go in there i think"):

Moved 21 top-level docs/*.md files into 7 new topic subdirectories
(existing web-ui/, turn-loop/, swarm/, tools/, crates/ untouched):
  getting-started/  setup.md
  agent-lifecycle/  agent-hierarchy.md, approvals.md, persistence.md
  trust-boundary/   boundary.md, security.md
  integrations/     forge.md, matrix.md, github.md, knowledge.md
  networking/       gateway.md, network.md, snapshot-store.md
  scheduler/        jobq.md, coordinator.md, ci.md, observability.md
  process/          conventions.md, gotchas.md, pr-review-gate.md
  web-ui/           terminal-rendering.md (moved into the EXISTING dir,
                    per mara's correction to the original getting-started
                    guess -- it's UI implementation detail, not onboarding)

The physical layout now matches docs/README.md's own topical headers,
which already amounted to this taxonomy -- see the scoping comment on
the issue for the two findings that motivated this (a genuine
duplication between CLAUDE.md's old "Reading paths" list and
docs/README.md's grouped one, since drifted out of sync with each
other; and the flat layout not matching the grouping we already had).

Fixed every cross-reference this moved across the whole repo (~120
files: docs/ internal links at every depth, Rust doc comments, nix
module option docs, crate READMEs) -- verified two ways: a grep sweep
confirming zero remaining references to any old path, and a script
that resolves every markdown link in docs/**/*.md + CLAUDE.md +
README.md against the filesystem and reports anything that doesn't
exist (zero broken links).

Collapsed CLAUDE.md's "Reading paths" section (the duplicate) down to
a pointer at docs/README.md, now the single index. Rewrote
docs/README.md itself to use the new subdirectory paths and added the
one doc it was missing that CLAUDE.md's old copy had (pr-review-gate.md).

Classified all 22 docs/*.md files first via a haiku subagent (mara's
suggestion) on two axes -- proposed grouping and operator-vs-
implementation focus -- before finalizing the taxonomy; spot-checked
the report and found internal inconsistencies (its classification
table disagreed with its own summary section for a few files), so this
taxonomy is my original proposal + the one correction mara gave
directly, not a blind application of the subagent's table. The
operator-focus data it gathered is still useful for a follow-up
content pass (docs skewing 'mixed' rather than pure operator-facing),
not addressed in this PR -- structure only.

nix fmt clean, both pre-push lints clean.
2026-09-02 01:55:37 +02:00
atlas
ca4b56c006 hive-c0re: stop naming nix implementation files in env-var prose
Six diagnostics and eight comments explained where an environment
variable comes from by naming the module file that sets it. The reader
of a panic is an operator, who does not edit that file; the reader of
the comment is already in the code. Neither can act on a filename, and
a filename in prose is checked by nothing.

Each one now names what the reader can act on: the option where there
is one (services.hyperhive.network.bridgeName, .bridgeIp,
.bridgePrefixLength, c0re.assets, c0re.orgAvatarPng), otherwise the
unit the process is expected to run under. Two of the six diagnostics
had a real option available; the other four have none, so they name
the unit rather than an invented path.

Four of them pointed at hive-c0re.nix, which is a directory. Two of
those spelled it nix/modules/hive-c0re.nix — a directory that does not
exist either, and one of the two was wrapped across a line break, so
no grep for the whole path could have found it. That is the argument
for the change: prose naming a file rots silently because nothing
compiles it.

Scope here is the four files around those diagnostics. The same
pattern still matches elsewhere in the tree; that sweep follows.
2026-08-30 14:13:11 +02:00
atlas
e15c499a31 fix(#3245): resolve the remaining intra-doc links in hive-c0re
Takes the crate from 26 rustdoc warnings to 1, on top of the ten in the
previous commit.

argus's review findings:
- agent_sockets.rs: [`write`] was still ambiguous (function vs macro).
  The previous change narrowed the qualifier and left the ambiguity;
  [`write()`] is what resolves it.
- forge/users.rs <hex> and stats/container_stats.rs <name>: unclosed
  HTML tags in prose, now backticked.

The rest of the crate, so the count actually reaches zero:
- job_queue/mod.rs: Queue::graph_snapshot -> JobQueue::graph_snapshot
  (there is no Queue type), and super::scheduler -> scheduler (mod.rs
  *is* job_queue, so super:: pointed outside it)
- job_queue/resource.rs: NodeKind -> super::model::NodeKind
- matrix.rs: password_path(name) -> password_path; and
  forge::provision_user_token -> crate::forge::provision_user_token.
  Note the path has no `users` segment: forge/mod.rs declares `mod
  users` private and re-exports it, so the canonical path comes from the
  re-export rather than the directory tree.
- socket_server/lifecycle_handlers.rs: InfraContainer ->
  hive_priv_sock::InfraContainer
- stats/otel_metrics.rs: crate::meta::otel_config is a private fn no
  path can name from another module, so it becomes prose
- main.rs: redundant explicit link target dropped

coordinator.rs:405 (CrashWatchGuard) is deliberately untouched: #3244
deletes that doc block, so fixing it here would conflict with an open PR
and repair a symbol that is about to stop existing.
2026-08-14 00:25:35 +02:00
iris
e525dcb6d4 agent icon: 404 when unconfigured, client-side fallback
hive_sh4re::assets::branding_svg() resolved a server-side default
icon at runtime from HIVE_ASSETS_DIR — the only consumer was
serve_icon(), which fell back to it whenever the agent had no
`hyperhive.icon` override. Removed both the fallback and the
function: serve_icon() now 404s when /etc/hyperhive/icon.svg is
absent, and the per-agent web UI (app.js) picks up the existing
dashboard swarm.js pattern — swap the <img> src to the
frontend-bundled /favicon.svg on load failure, guarded against
looping if the fallback itself 404s.

Updated the doc/comment claims that said the server always returns
an image (docs/web-ui/agent.md, nix/agent-modules/default.nix, the
hive-c0re/forge/users.rs comment referencing the old shared-asset
set). forge-avatar-sync and the matrix avatar sync are unaffected —
both are gated on hyperhive.icon != null and never depended on the
removed fallback.
2026-08-10 20:59:59 +02:00
iris
3e388d2d46 move avatar-PNG helpers to hive-core, configurable org avatar
core_avatar_png() and config_org_avatar_png() were hive-sh4re
functions used only by hive-c0re::forge::users. Move them there as
private path-resolution helpers (core_avatar_png_path,
config_org_avatar_png_path), following the existing
std::env::var(...).expect(...) required-env-var style used elsewhere
in hive-c0re.

The org avatar is now independently configurable:
services.hyperhive.c0re.orgAvatarPng (nullable path, default null)
lets an operator override just the agent-configs org avatar PNG
without replacing the whole assets package. Wired via a new
HIVE_ORG_AVATAR_PNG env var that falls back to the bundled PNG when
the option is unset. The core avatar stays under HIVE_ASSETS_DIR,
unchanged.
2026-08-09 23:33:00 +02:00
atlas
1f8cfdabd9 fix(#2911): stop putting the minted forge token in an error string
mint_token interpolated forgejo's raw stdout into its anyhow context on
the parse-failure path, and on that call stdout carries the access token
that was just created. The happy path below it is careful to log only
the user and token names; the error path handed the secret over whole.

It fires exactly when forgejo's output format drifts, which is the same
drift that breaks extract_token in the first place -- so the "help me
debug this" context printed the secret it had failed to find.

Report the shape of the output (bytes, lines) instead of its contents.
That is what diagnoses a version drift anyway: you want to know forgejo
printed something with no token-shaped word in it, not the bytes.

Redaction at a logging call site does not cover the error path.
with_context and bail! are output channels too.
2026-08-02 04:44:22 +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
4162f557c9 refactor(#2285): repoint knowledge + forge core-token to paths.rs consts 2026-07-10 20:32:03 +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