matrix: activationScript pre-creates token + share reqwest client across sweep (argus #565 nits)
This commit is contained in:
parent
50ceb929d7
commit
dfec976461
2 changed files with 44 additions and 9 deletions
|
|
@ -225,7 +225,15 @@ fn extract_access_token(body: &serde_json::Value) -> Result<String> {
|
||||||
/// homeserver. Skips registration entirely if the token file already
|
/// homeserver. Skips registration entirely if the token file already
|
||||||
/// exists (treating a present token as proof the account is good).
|
/// exists (treating a present token as proof the account is good).
|
||||||
/// To force re-registration, delete the token file.
|
/// To force re-registration, delete the token file.
|
||||||
pub async fn ensure_user_for(name: &str, register_token: &str) -> Result<()> {
|
///
|
||||||
|
/// `client` is shared across the sweep so we build one reqwest
|
||||||
|
/// connection pool for all agents rather than one per call (argus
|
||||||
|
/// nit on #565 — bounded but wasteful).
|
||||||
|
pub async fn ensure_user_for(
|
||||||
|
client: &reqwest::Client,
|
||||||
|
name: &str,
|
||||||
|
register_token: &str,
|
||||||
|
) -> Result<()> {
|
||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
let path = token_path(name);
|
let path = token_path(name);
|
||||||
if path.exists()
|
if path.exists()
|
||||||
|
|
@ -235,11 +243,7 @@ pub async fn ensure_user_for(name: &str, register_token: &str) -> Result<()> {
|
||||||
tracing::debug!(%name, "matrix: token already present");
|
tracing::debug!(%name, "matrix: token already present");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let client = reqwest::Client::builder()
|
let access_token = register_user(client, name, register_token).await?;
|
||||||
.timeout(std::time::Duration::from_secs(HTTP_TIMEOUT_SECS))
|
|
||||||
.build()
|
|
||||||
.context("matrix: build HTTP client")?;
|
|
||||||
let access_token = register_user(&client, name, register_token).await?;
|
|
||||||
if let Some(parent) = path.parent() {
|
if let Some(parent) = path.parent() {
|
||||||
std::fs::create_dir_all(parent).ok();
|
std::fs::create_dir_all(parent).ok();
|
||||||
}
|
}
|
||||||
|
|
@ -253,8 +257,8 @@ pub async fn ensure_user_for(name: &str, register_token: &str) -> Result<()> {
|
||||||
/// Per-agent matrix sync: ensure the agent has a matrix account + token.
|
/// Per-agent matrix sync: ensure the agent has a matrix account + token.
|
||||||
/// All operations are idempotent; failures are logged as warnings but
|
/// All operations are idempotent; failures are logged as warnings but
|
||||||
/// don't abort the caller.
|
/// don't abort the caller.
|
||||||
pub async fn sync_agent(name: &str, register_token: &str) {
|
pub async fn sync_agent(client: &reqwest::Client, name: &str, register_token: &str) {
|
||||||
if let Err(e) = ensure_user_for(name, register_token).await {
|
if let Err(e) = ensure_user_for(client, name, register_token).await {
|
||||||
tracing::warn!(%name, error = ?e, "matrix: ensure_user failed");
|
tracing::warn!(%name, error = ?e, "matrix: ensure_user failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -276,6 +280,18 @@ pub async fn ensure_all() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// One HTTP client for the whole sweep — connection pool is
|
||||||
|
// reused across agents.
|
||||||
|
let client = match reqwest::Client::builder()
|
||||||
|
.timeout(std::time::Duration::from_secs(HTTP_TIMEOUT_SECS))
|
||||||
|
.build()
|
||||||
|
{
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(e) => {
|
||||||
|
tracing::warn!(error = ?e, "matrix: build HTTP client failed; skipping sweep");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
let Ok(containers) = crate::lifecycle::list().await else {
|
let Ok(containers) = crate::lifecycle::list().await else {
|
||||||
tracing::warn!("matrix: nixos-container list failed; skipping user sweep");
|
tracing::warn!("matrix: nixos-container list failed; skipping user sweep");
|
||||||
return;
|
return;
|
||||||
|
|
@ -288,7 +304,7 @@ pub async fn ensure_all() {
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
sync_agent(&name, ®ister_token).await;
|
sync_agent(&client, &name, ®ister_token).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -181,6 +181,25 @@ in
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# Generate the registration token at system activation time, BEFORE
|
||||||
|
# the hive-matrix container would otherwise start with an empty
|
||||||
|
# bind-mount target (argus nit on #565: nspawn creates an empty
|
||||||
|
# file when the host path is missing, tuwunel reads it as
|
||||||
|
# `registration_token_file=""` and rejects every registration
|
||||||
|
# until the next restart). Idempotent: only writes when the file
|
||||||
|
# doesn't exist. 32-byte hex = 64 chars, same shape hive-c0re's
|
||||||
|
# `matrix::ensure_register_token` would produce.
|
||||||
|
system.activationScripts.hive-matrix-register-token = lib.stringAfter [ "var" ] ''
|
||||||
|
tokenFile=${lib.escapeShellArg (toString cfg.registrationTokenFile)}
|
||||||
|
if [ ! -s "$tokenFile" ]; then
|
||||||
|
mkdir -p "$(dirname "$tokenFile")"
|
||||||
|
head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n' > "$tokenFile"
|
||||||
|
echo >> "$tokenFile"
|
||||||
|
chmod 600 "$tokenFile"
|
||||||
|
echo "hive-matrix: generated registration token at $tokenFile"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
|
||||||
containers.hive-matrix = {
|
containers.hive-matrix = {
|
||||||
autoStart = true;
|
autoStart = true;
|
||||||
ephemeral = false;
|
ephemeral = false;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue