agents: pull the forge token from bao; drop tea-login
forge-token.nix fetches swarm/agents/<agent>/forge-token under the agent's own store identity into /run/hive-agent-forge-token/token, and re-fetches on a timer so a rotation lands. hive-forge, the git credential helper, hive-forge-notify, forge-avatar-sync and the web UI read that file first and fall back to <state>/forge-token. tea-login is deleted: it copied the token into ~/.config/tea, which docs/swarm/credentials.md forbids for a store secret. hive-forge covers the same verbs. swarmctl gains agent mint-forge-token. Refs #3782
This commit is contained in:
parent
52c8c0b0de
commit
dd32a395f7
16 changed files with 501 additions and 156 deletions
|
|
@ -37,23 +37,17 @@ async fn forgejo_loop(state_dir: String, socket: std::path::PathBuf) {
|
|||
}
|
||||
};
|
||||
|
||||
let token_path = format!("{state_dir}/forge-token");
|
||||
// Retry reading the token to handle races where hive-priv provisions
|
||||
// it after the harness starts, or where a parent-container chown
|
||||
// briefly makes the file unreadable.
|
||||
let token = {
|
||||
let token_paths = hive_forge_notify::forge_token_paths(&state_dir);
|
||||
// Retry reading the token to handle races where the agent's fetch of
|
||||
// it from the swarm secret store lands after this unit starts, or where
|
||||
// a parent-container chown briefly makes the file unreadable.
|
||||
let mut token = {
|
||||
let mut attempts = 0u32;
|
||||
loop {
|
||||
match tokio::fs::read_to_string(&token_path).await {
|
||||
Ok(t) => {
|
||||
let t = t.trim().to_owned();
|
||||
if !t.is_empty() {
|
||||
break t;
|
||||
}
|
||||
debug!("forge_notify: empty forge token at {token_path}");
|
||||
}
|
||||
Err(e) => debug!("forge_notify: cannot read token at {token_path}: {e}"),
|
||||
if let Some(t) = hive_forge_notify::read_first_token(&token_paths) {
|
||||
break t;
|
||||
}
|
||||
debug!(?token_paths, "forge_notify: no forge token yet");
|
||||
attempts += 1;
|
||||
if attempts >= TOKEN_RETRY_MAX {
|
||||
debug!(
|
||||
|
|
@ -65,7 +59,7 @@ async fn forgejo_loop(state_dir: String, socket: std::path::PathBuf) {
|
|||
}
|
||||
};
|
||||
|
||||
let Some(source) = ForgejoSource::new(&forge_url, &token) else {
|
||||
let Some(mut source) = ForgejoSource::new(&forge_url, &token) else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
|
@ -111,6 +105,18 @@ async fn forgejo_loop(state_dir: String, socket: std::path::PathBuf) {
|
|||
|
||||
loop {
|
||||
interval.tick().await;
|
||||
// The swarm rotates the token when it goes stale, and the agent's
|
||||
// fetch replaces the file; pick the new one up rather than polling
|
||||
// with a revoked token for the rest of this process's life.
|
||||
if let Some(t) = hive_forge_notify::read_first_token(&token_paths)
|
||||
&& t != token
|
||||
&& let Some(s) = ForgejoSource::new(&forge_url, &t)
|
||||
{
|
||||
info!("forge_notify: forge token changed on disk; using the new one");
|
||||
token = t;
|
||||
source = s;
|
||||
own_login.clear();
|
||||
}
|
||||
if own_login.is_empty() {
|
||||
own_login = resolve_own_login(&client, &source).await;
|
||||
if !own_login.is_empty() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue