manager_server: reject proposals that modify flake.nix
submit_apply_commit now diffs the freshly-tagged proposal/<id> against applied/main and refuses if flake.nix is in the changeset. flake.nix is fixed boilerplate the meta flake depends on (it exports nixosModules.default = import ./agent .nix); silent edits there would break the nixosConfiguration in subtle ways. the manager prompt already says don't touch it; this is the host-side belt — clear error to the manager on submit, row marked failed in sqlite, no orphan pending approval to chase. diff-failure is logged + ignored: the build path surfaces concrete errors if flake.nix is actually broken.
This commit is contained in:
parent
68ef6ab433
commit
6b3ef4549c
2 changed files with 50 additions and 8 deletions
8
TODO.md
8
TODO.md
|
|
@ -74,14 +74,6 @@ Pick anything from here when relevant. Cross-cutting design notes live in
|
|||
once a terminal sibling lands — would keep the audit
|
||||
trails browsable without forever-growth.
|
||||
|
||||
- **Reject proposals that touch `flake.nix`.** The manager's
|
||||
prompt says don't edit it, but nothing on the host side
|
||||
enforces. Add a check in
|
||||
`manager_server::submit_apply_commit`: after fetching the
|
||||
proposal sha into applied, `git diff-tree <sha> -- flake.nix`
|
||||
— non-empty diff → refuse + clear error message. Cheap
|
||||
belt-and-suspenders.
|
||||
|
||||
- **Inert `nix flake lock` no-args call in `meta::sync_agents`.**
|
||||
Still valid in current nix (resolves missing inputs without
|
||||
bumping existing ones) but parallel to the deprecated
|
||||
|
|
|
|||
|
|
@ -324,6 +324,26 @@ async fn submit_apply_commit(
|
|||
return Err(anyhow::anyhow!("git_fetch_to_tag: {e:#}"));
|
||||
}
|
||||
};
|
||||
// Reject proposals that touch flake.nix — that file is fixed
|
||||
// boilerplate the meta flake depends on (it exports
|
||||
// `nixosModules.default = import ./agent.nix`); a manager edit
|
||||
// there silently breaks the nixosConfiguration. Prompt tells the
|
||||
// manager not to; this is the host-side belt.
|
||||
let proposal_ref = format!("refs/tags/{tag}");
|
||||
match proposal_modifies(&applied_dir, &proposal_ref, "flake.nix").await {
|
||||
Ok(true) => {
|
||||
let note = "proposal modifies flake.nix — that file is hive-c0re-owned \
|
||||
boilerplate; edit only agent.nix (and sibling modules)";
|
||||
let _ = coord.approvals.mark_failed(id, note);
|
||||
return Err(anyhow::anyhow!(note));
|
||||
}
|
||||
Ok(false) => {}
|
||||
Err(e) => {
|
||||
// Diff itself failed — log + continue. The build will
|
||||
// surface concrete errors if flake.nix is actually borked.
|
||||
tracing::warn!(error = ?e, %tag, "flake.nix tamper-check failed; allowing");
|
||||
}
|
||||
}
|
||||
coord
|
||||
.approvals
|
||||
.set_fetched_sha(id, &sha)
|
||||
|
|
@ -331,6 +351,36 @@ async fn submit_apply_commit(
|
|||
Ok((id, sha))
|
||||
}
|
||||
|
||||
/// True iff `proposal_ref`'s tree differs from `refs/heads/main` at
|
||||
/// `path`. Used to enforce that proposals don't touch hive-c0re-owned
|
||||
/// files in the applied repo.
|
||||
async fn proposal_modifies(
|
||||
applied_dir: &std::path::Path,
|
||||
proposal_ref: &str,
|
||||
path: &str,
|
||||
) -> anyhow::Result<bool> {
|
||||
let out = crate::lifecycle::git_command()
|
||||
.current_dir(applied_dir)
|
||||
.args([
|
||||
"diff",
|
||||
"--name-only",
|
||||
&format!("refs/heads/main..{proposal_ref}"),
|
||||
"--",
|
||||
path,
|
||||
])
|
||||
.output()
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("spawn git diff: {e}"))?;
|
||||
if !out.status.success() {
|
||||
anyhow::bail!(
|
||||
"git diff exited {}: {}",
|
||||
out.status,
|
||||
String::from_utf8_lossy(&out.stderr).trim()
|
||||
);
|
||||
}
|
||||
Ok(!out.stdout.iter().all(u8::is_ascii_whitespace))
|
||||
}
|
||||
|
||||
/// On `AskOperator { ttl_seconds: Some(n) }`, sleep n seconds and then
|
||||
/// try to resolve the question with `[expired]`. If the operator (or
|
||||
/// any other path) already answered it, `answer()` returns Err and
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue