fix: route forge_admin through hive-priv; auto-recover matrix passwords

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
This commit is contained in:
atlas 2026-06-03 23:17:39 +02:00
commit 34bc4c0b06
5 changed files with 147 additions and 47 deletions

View file

@ -95,44 +95,15 @@ pub async fn is_present() -> bool {
/// forgejo user (the only uid with write access to the state dir).
/// Returns stdout on success; bails with stderr context on failure.
async fn forge_admin(args: &[&str]) -> Result<String> {
let mut cmd = Command::new("nixos-container");
// `runuser` (util-linux, always present in a NixOS container)
// beats `sudo` here — sudo isn't installed unless `security.sudo`
// is enabled, and we don't want to depend on that.
//
// `--work-path` is mandatory: without it, the admin CLI defaults
// WorkPath to `dirname(executable)` (a RO nix-store path), then
// looks for `<WorkPath>/custom/conf/app.ini` which doesn't
// exist, falls back to defaults, and F3 init tries to mkdir
// under the nix store and fatals. The systemd unit sets
// WORK_PATH for the daemon; we mirror it here for the CLI.
cmd.args([
"run",
FORGE_CONTAINER,
"--",
"runuser",
"-u",
"forgejo",
"--",
"forgejo",
"--work-path",
"/var/lib/forgejo",
"admin",
]);
cmd.args(args);
let out = cmd
.output()
.await
.context("invoke nixos-container run hive-forge -- forgejo admin")?;
if !out.status.success() {
anyhow::bail!(
"forgejo admin {} failed ({}): {}",
args.join(" "),
out.status,
String::from_utf8_lossy(&out.stderr).trim(),
);
}
Ok(String::from_utf8_lossy(&out.stdout).into_owned())
// Route through hive-priv (root helper) because `nixos-container run`
// uses nsenter to enter the container's namespaces, which requires root.
// hive-c0re runs as the unprivileged `hive-core` user and cannot call
// nsenter directly — doing so produces:
// nsenter: stat of /proc/<pid>/ns/user failed: Permission denied
let (stdout, _stderr) = crate::priv_client::run_forge_admin(args).await.with_context(
|| format!("forgejo admin {} (via hive-priv)", args.join(" ")),
)?;
Ok(stdout)
}
/// Pull the access token out of forgejo's success message. Format