summaryrefslogtreecommitdiff
path: root/machines
diff options
context:
space:
mode:
Diffstat (limited to 'machines')
-rw-r--r--machines/enzo/default.nix103
1 files changed, 103 insertions, 0 deletions
diff --git a/machines/enzo/default.nix b/machines/enzo/default.nix
index f72522c..d14dacd 100644
--- a/machines/enzo/default.nix
+++ b/machines/enzo/default.nix
@@ -28,6 +28,7 @@
extraGroups = [
"networkmanager"
"wheel"
+ "ikai"
];
packages = with pkgs; [
git
@@ -45,6 +46,108 @@
];
};
+ # --- Sandbox account (yokai) ---
+ # Model: LOCAL CLONES, not a shared working tree. yokai owns its own clone of
+ # each repo under /srv/git; hwebs keeps the canonical repo in ~. Work moves
+ # between the two over git remotes — never by sharing one tree.
+ #
+ # Why not one shared tree: git executes hooks and config out of .git, so a
+ # tree yokai can write must never be one hwebs runs `git` *inside* — a planted
+ # hook/config would run as hwebs and escape the sandbox. Git's "dubious
+ # ownership" warning is exactly that guard; don't defeat it with safe.directory
+ # on a yokai-writable repo.
+ #
+ # CARDINAL RULE: hwebs never runs git with CWD inside /srv/git/<repo>. Review
+ # the agent's work by FETCHING its commits into your own repo and reading them
+ # there.
+ #
+ # /srv/git is created below as 2770 root:ikai — group-writable + setgid, so
+ # hwebs and yokai (both in ikai) can each drop clones without sudo and new
+ # files inherit the ikai group. It sits outside ~ so /home/hwebs stays 700
+ # and yokai never needs to traverse into home. (Tradeoff: yokai has write on
+ # the /srv/git dir itself, i.e. its own play area — fine for a sandbox.)
+ #
+ # Seed the agent's clone (hand a fresh clone to yokai):
+ # git clone ~/git/<repo> /srv/git/<repo>
+ # sudo chown -R yokai:ikai /srv/git/<repo> # yokai now owns its tree
+ # TODO: wrap seed (clone + chown) in a one-shot helper (shell fn / just).
+ #
+ # Pull the agent's work back into YOUR repo (stay in your trusted tree):
+ # git -C ~/git/<repo> remote add sandbox /srv/git/<repo>
+ # git -C ~/git/<repo> fetch sandbox
+ # git -C ~/git/<repo> log --oneline sandbox/main # review, then merge
+ #
+ # Land + SIGN on main (signatures attest that hwebs reviewed+vouches, so hwebs
+ # signs — never put a signing key in yokai's home, and never give yokai yours).
+ # Squash the agent's WIP into one commit you author + sign; satisfies "require
+ # signed commits" branch protection trivially and keeps history clean:
+ # git -C ~/git/<repo> checkout main
+ # git -C ~/git/<repo> merge --squash sandbox/main # stage, don't commit
+ # git -C ~/git/<repo> commit -S -m "...\n\nAssisted-by: claude-code:<model>"
+ # git -C ~/git/<repo> push origin main
+ # Alt — keep granular commits instead of squashing: rebase re-commits each
+ # one signed by hwebs (also all-signed, but review commit-by-commit):
+ # git -C ~/git/<repo> checkout -b land sandbox/main # local branch at tip
+ # git -C ~/git/<repo> rebase -S main # replay onto main, sign each
+ # git -C ~/git/<repo> checkout main
+ # git -C ~/git/<repo> merge --ff-only land && git -C ~/git/<repo> branch -d land
+ # git -C ~/git/<repo> push origin main
+ # Alt — INTERACTIVE, for exploring/curating the agent's history by hand: opens
+ # a todo list (pick/squash/fixup/reword/drop/reorder per commit), then the
+ # message editor. Good when you want to hand-pick what lands. Run it in a real
+ # terminal — interactive git can't be driven through the agent harness:
+ # git -C ~/git/<repo> checkout -b land sandbox/main
+ # git -C ~/git/<repo> rebase -i -S main # -S signs each resulting commit
+ # # ...then merge --ff-only land into main + push, as above.
+ # (Signing config lives in hwebs's dotfiles, not here: commit.gpgsign +
+ # user.signingkey.)
+ #
+ # Airtight variant — exchange via bundles (inert data, no hooks/config run):
+ # (yokai) git -C /srv/git/<repo> bundle create /srv/git/x.bundle main
+ # (hwebs) git -C ~/git/<repo> fetch /srv/git/x.bundle main:sandbox/main
+ #
+ # Run the agent as yokai: sudo -u yokai claude
+ #
+ # Let hwebs drop to yokai without a password. This grants NO new privilege —
+ # it's a de-escalation to a weaker account — so NOPASSWD here is low-risk,
+ # unlike a run-as-root rule. Scoped to runAs=yokai only; root stays gated.
+ security.sudo.extraRules = [
+ {
+ users = [ "hwebs" ];
+ runAs = "yokai";
+ commands = [
+ {
+ command = "ALL";
+ options = [
+ "NOPASSWD"
+ "SETENV"
+ ];
+ }
+ ];
+ }
+ ];
+
+ users.groups.ikai = { };
+ users.users.yokai = {
+ isNormalUser = true;
+ description = "Sandbox account for coding agents";
+ group = "ikai";
+ packages = with pkgs; [
+ git
+ claude-code
+ pi-coding-agent
+ ];
+ };
+
+ # Drop dir for the agent's local clones. Created at activation as 2770
+ # root:ikai — setgid (new entries inherit ikai) + group-writable so hwebs and
+ # yokai can each place clones here without sudo; world sees nothing. Sits
+ # outside ~ so /home/hwebs stays a sealed 700. Trailing "-" = no age-cleaning.
+ # Each clone's own ownership is set when seeded (see the notes above).
+ systemd.tmpfiles.rules = [
+ "d /srv/git 2770 root ikai -"
+ ];
+
nix.settings.trusted-users = [
"root"
"hwebs"