The implementation now queries ActiveState and dispatches reload/
reset-start/start rather than blindly calling systemd-run nginx -s reload.
Clarify that machine-bus transport requires root.
Reflect the fix from the companion Rust change: the nginx reload/start
is now routed through hive-priv (root) instead of calling
systemd-run --machine= directly from unprivileged hive-c0re.
Document the state-aware dispatch (active→reload, failed→reset+start,
other→start).
systemctl --machine=hive-gateway requires root (machine-bus transport
enters the container namespace). hive-c0re is unprivileged, so every
call to nginx_active_state() and gateway_systemctl() silently failed
with exit 1, causing a continuous 30s retry loop without ever
syncing nginx.
Fix:
- Move state-aware nginx logic into hive-priv ReloadGatewayNginx:
check ActiveState, then reload/reset-start/start accordingly.
hive-priv already runs as root and has machine-bus rights.
- Remove nginx_active_state() and gateway_systemctl() from
gateway_nginx.rs (they were always running unprivileged, always
failing silently).
- Make write(), reload_if_pending(), reload_gateway_nginx() async so
they can call the async priv_client without a blocking bridge.
- Update callers in agent_sockets::spawn_poll and meta::sync_agents
to await the now-async functions.
The priv_client::reload_gateway_nginx() call and PrivRequest::ReloadGatewayNginx
wire type already existed — the gateway_nginx module was just not using them.
matrix-avatar-sync ran on every boot (RemainAfterExit=false + path
trigger), uploading a fresh PNG each time. Every upload mints a new
mxc:// URI, which triggers a profile state event in every joined room
— resulting in timeline spam even when the avatar hasn't changed.
Fix: before uploading, compute sha256sum of /etc/hyperhive/icon.svg
and compare against the last-synced hash stored in
$HYPERHIVE_STATE_DIR/matrix-avatar-icon-hash. Skip the upload if the
hash matches. Write the hash after a successful avatar_url PUT so
subsequent boots are no-ops until the icon file changes.
Hash file lives in the agent's state dir — survives restart, cleared
on purge (so purge + re-provision gets a fresh upload). Delete to force
re-upload manually.
Closes#1231
forge_admin() spawned nixos-container run hive-forge directly from the
hive-core process. nixos-container run uses nsenter to enter the container
namespaces, which requires root. hive-core is unprivileged, so every call
failed with: nsenter: stat of /proc/<pid>/ns/user failed: Permission denied
Fix: add RunForgeAdmin { args } to PrivRequest. hive-priv (root) handles
it by spawning nixos-container run hive-forge -- runuser -u forgejo --
forgejo --work-path /var/lib/forgejo admin <args>. forge_admin() now calls
priv_client::run_forge_admin().
matrix: ensure_user_for hit M_USER_IN_USE then failed when the stored
password file was missing (state dirs wiped but homeserver kept accounts).
Previously required manual hivectl matrix reset-password <name>.
Fix: add auto_reset_password() — calls the admin API (PUT
/_synapse/admin/v2/users/@<name>:<server> with the hive admin token) to
set a new random password, then proceeds with login. Falls back to the
existing manual-recovery error if the admin token is unavailable.
Closes#1234
On first boot (or after a wipe) hive-c0re writes forge-core-token only
after the forge container starts and the admin is provisioned. This
lags hive-c0re.service becoming active. The previous code bailed
immediately with TOKEN=placeholder if the token file was absent,
causing the runner to fail registration with 'token not found'.
Fix: merge both waits (core-token file appearance + forge API ready)
into a single 60s retry loop. The early-bail path is removed; the
script only exits cleanly if .runner is valid (writes placeholder)
or a fresh registration token is obtained.
When .runner exists but core-token is absent after 60s, we keep the
existing credentials (safe — the runner holds valid creds; next boot
will validate properly).
Closes#1224 (which tracks #1221).
Write mcp-loose-ends/matrix.json on invite arrival (before wake) and
after join_room clears an invite. The harness scans mcp-loose-ends/
generically in get_loose_ends, so pending invites are visible there
without any harness-side changes.
- paths: add mcp_loose_ends_dir() (mirrors hive-bash-mcp pattern)
- handlers: add refresh_invite_loose_ends() — atomic tmp+rename write
- handlers: join_room calls refresh after successful join to clear entry
- timeline: install_invite_handler refreshes loose-ends before wake
- drop stale --server flag reference from ensure_admin_user doc comment
- simplify M_USER_IN_USE recovery message: point at hivectl commands
- add #[must_use] to admin_token_path()
- rename tracing field rename_error in migration warn log (was error,
which held rename err but fired on read failure — misleading)
Add matrix::discover_server_name() via GET /_matrix/key/v2/server
(unauthenticated federation endpoint, always returns server_name).
hivectl is always talking to the local hive — no reason to require
the operator to spell out the server_name.
- provision @hive:<server> as the first matrix account in ensure_all()
(Conduit/tuwunel makes the first registered user admin automatically)
- add hivectl matrix sync-admin — manual re-provision of the admin token
- add hivectl matrix promote-user <name> --server <name> — promote via
Synapse-compat admin API using the hive admin token
- add hivectl matrix reset-password <name> --server <name> — reset an
agent's password + persist it so ensure_user_for can re-login; follow
with hivectl matrix create-user <name> to mint a fresh access token
- both commands fall back to HYPERHIVE_MATRIX_SERVER_NAME env var for
--server when omitted
The password file was stored at agent_notes_dir/matrix-password which
lives inside agent_state_root — wiped by destroy --purge. On re-spawn
with the same agent name, the matrix user still exists in the homeserver
but the stored password is gone, making re-login impossible.
Move password to /var/lib/hyperhive/matrix-creds/<name>-password which
is not deleted by purge. On re-spawn, ensure_user_for finds M_USER_IN_USE,
reads the preserved password, re-logins, and writes a fresh token.
Also:
- add one-time migration that moves existing passwords from the old path
to the new location on first access after upgrade
- remove chown_to_agent on the password file (it is now host-only, not
inside the agent bind-mount tree)
- fix the error message to give actionable recovery steps instead of
suggesting hivectl matrix create-user --password which is rejected for
agent accounts
The gitea-actions-runner's host-scheme job processes use the
service's environment PATH, not the NixOS login-shell PATH.
Without this, 'nix flake check' and similar steps fail with
'nix: command not found'.
- add pkgs.nix to environment.systemPackages (ensures the binary
is in /run/current-system/sw/bin)
- set explicit PATH on gitea-runner-hive service covering both
/run/current-system/sw/bin and /nix/var/nix/profiles/default/bin
Instead of requiring a click on the load-more pill, trigger loadMore()
automatically when the user scrolls within LOAD_MORE_SCROLL_PX (80px)
of the top of the log. The pill stays as a visual indicator that more
history is available.