Irreversible needs a human; run outside the terminal without increasing the blast radius
Sending, publishing, deleting, and spending can't be undone. In the loop, these actions become requests that wait at a gate that only you open, seeing the destination and content. And when the loop leaves the terminal for systemd, it takes the cap, the sandbox, and the gate with it, not your whole session.
3systemd with a time and memory cap and the Codex sandbox inside
4Cloud Routines: the same care, without confirming details
Goal: put sending, push, and deleting behind a gate that only the human opens, and run the loop under systemd with a cap and sandbox.
01 · The gate to the irreversible
Click each step.
Click each block in the diagram to see what it does.
It's N1 of the POLITICA (lesson 4): the agent prepares, the human executes.
02 · Watch the queue stop at the gate
The loop's nightly cycle. The send action is irreversible: on the first run it stops and waits. Approve (resume) and it goes on.
queued
running
done
waiting for human
Illustrative simulation. The real gate is in the rehearsal just below.
03 · Rehearsal: the gate from start to finish
Request, try without approval, approve without a terminal, approve "as a human", tamper afterward, approve again, execute, and try to reuse. No model; the send is a line in saida.log.
pedido 20261019-065013-4181: enviar-email → cliente@exemplo.invalid (30 caracteres). Aguardando aprovação humana.
RECUSADO: 20261019-065013-4181 sem aprovação humana
saída: 1
RECUSADO: aprovar exige um terminal humano (stdin não é tty)
saída: 1
APROVADO 20261019-065013-4181 (hash 50a9d2859698c516)
RECUSADO: o conteúdo de 20261019-065013-4181 mudou depois da aprovação
saída: 1
APROVADO 20261019-065014-b7be (hash 50a9d2859698c516)
EXECUTADO 20261019-065014-b7be: enviar-email → cliente@exemplo.invalid
saída: 0
RECUSADO: 20261019-065014-b7be sem aprovação humana
saída: 1
1
Four refusals and one execution; the 1 at the end is the only line in saida.log. The ids change on every run.
In the rehearsal, the script fakes typing the id into a pseudo-terminal. In real life, you run python3 porta.py aprovar [id] in your terminal, read the content, and type it. Swap the rehearsal's "executar" for your real send.
04 · A real policy, anonymized
Irreversible-action rules from the global CLAUDE.md of an INEMA machine, and lessons that became rules.
1. Removing asks, every time
A published deletion or removal requires individual approval for each one, with the target identified beforehand. Generic, batch, or publishing authorization does not count as deletion approval.
Sending email and deleting an event are irreversible: confirm first.
2. A key is not permission
No API without explicit authorization for that service and purpose. Having the credential in .env does not authorize using it; a request for a result ("do it", "keep going") does not either.
The rule applies to scripts, subagents, and any intermediary tool.
3. Stop and call
In a long loop, stop and call the human: credit or API spend, irreversible or external action, missing credential, business decision.
And two rules for getting along: don't delete what you didn't create; don't kill a process you didn't create.
Real lesson (10/15/2026)Kill a process only after readlink /proc/[pid]/cwd shows it's yours: another session's server was taken down by mistake, thinking it was your own.
4. Undoing is not going back
git checkout [file] goes back to the last commit, not to the state before the test: on 05/10/2026 it erased uncommitted rules. Destructive test: commit first or keep a copy outside.
That's why the guard in lesson 5 denies checkout -- . and reset --hard.
05 · Outside the terminal: cap and sandbox together
systemd gives the cap (time, memory); the Codex sandbox, inside it, gives the isolation that the user systemd did not provide on this machine (lesson 6).
Terminal · cap and isolation in systemd
P=~/projetos/loop-r-eng-seguranca-v72/kit/prova-isolamento.sh
W=/tmp/ensaio-seg/iso; mkdir -p $W
echo "## teto de tempo: RuntimeMaxSec=3 num sleep 10"
systemd-run --user --wait --pipe --quiet -p RuntimeMaxSec=3 sleep 10; echo "saída: $?"
echo "## fora do terminal, com teto E sandbox do Codex dentro"
bash $P systemd-run --user --pipe --wait --quiet -p MemoryMax=1G -p RuntimeMaxSec=60 -- \
codex sandbox -P :workspace -C $W --
echo "saída: $?"
File · kit/systemd/loop-seguro.service (template; comments translated for reading)
# loop-seguro.service — user unit template for running one agent cycle outside the terminal
# without increasing the blast radius. Replace MEU-PROJETO and check with prova-isolamento.sh BEFORE turning it on.
# cp loop-seguro.service ~/.config/systemd/user/ && systemctl --user daemon-reload
# systemctl --user start loop-seguro.service && journalctl --user -u loop-seguro -n 20
#
# What really holds (tested on 19/10/2026, systemd 255, Ubuntu, kernel with
# apparmor_restrict_unprivileged_userns=1):
# - caps: MemoryMax, TimeoutStartSec (oneshot does not accept RuntimeMaxSec), timeout and flock (they work in systemd --user)
# - isolation: the Codex sandbox (-s workspace-write) INSIDE the service
# What did NOT hold on this machine: PrivateNetwork=, ProtectHome=, IPAddressDeny= in systemd --user.
# The journal says "proceeding without" and the service runs without the lock. Don't count on them without proof.
[Unit]
Description=One cycle of the agent loop (Autonomous Loop Security course)
[Service]
Type=oneshot
WorkingDirectory=%h/projetos/MEU-PROJETO
# Fixed PATH: the service does not inherit your shell (or your keys). Do not put an EnvironmentFile with a secret.
Environment=PATH=%h/.npm-global/bin:/usr/local/bin:/usr/bin:/bin
StandardInput=file:%h/projetos/MEU-PROJETO/longrun/prompt.md
ExecStart=/usr/bin/flock -n %t/loop-seguro.lock timeout 2h codex exec -s workspace-write --skip-git-repo-check -C %h/projetos/MEU-PROJETO -
MemoryMax=4G
TimeoutStartSec=3h
NoNewPrivileges=yes
Example from the repository
## teto de tempo: RuntimeMaxSec=3 num sleep 10
saída: 1
## fora do terminal, com teto E sandbox do Codex dentro
escreve-dentro ABERTO
escreve-fora BLOQUEADO
rede BLOQUEADO
le-segredo ABERTO
aviso: lê arquivos fora da pasta; tire segredos do alcance ou corte a rede
ISOLAMENTO: OK
saída: 0
The sleep 10 was killed at 3 s (exit 1). Inside the same systemd-run, the Codex sandbox blocked writes outside and the network.
In a Type=oneshot service, RuntimeMaxSec= has no effect: systemd-analyze --user verify warns "RuntimeMaxSec= has no effect in combination with Type=oneshot. Ignoring." In the unit template, the cap is TimeoutStartSec= plus the timeout on the command line.
06 · Routines and other schedulers
What can be said with confidence.
1. What is known
There are reports (secondary source, not confirmed in the docs) of scheduled agents in the Claude Code cloud, the routines, triggered by a schedule, an API call, or a GitHub event.
The current limits were not confirmed for this course: treat it as a feature to check in the docs before using it.
2. The same rule
Wherever the loop runs, it carries only what the step needs: no access to your keys, no network where it reads private data, no irreversible action outside the gate.
Scheduling does not change the Rule of Two; it only takes you away from the loop. More reason for the gate.
07 · Practice
The gate, systemd, and your list of irreversible actions.
E18 · The gate in the rehearsal
10 min
Run the gate rehearsal. Then approve a request yourself, in your own terminal: python3 porta.py pedir, aprovar and executar.
Done when: the rehearsal shows four RECUSADO lines, one EXECUTADO, and ends with 1; your executar prints EXECUTADO once and, when repeated, sem aprovação humana.
E19 · Cap and sandbox in systemd
10 min
Run the systemd block on your machine (Linux with systemd).
Done when: the first step ends with saída: 1 (killed by the cap) and the second with ISOLAMENTO: OK and saída: 0.
E20 · The list of your irreversible actions
15 min
Write in the project's AGENTS.md (or CLAUDE.md) the list of actions in your loop that can't be undone and the path for each one: porta.py, the deny from lesson 4, or the guard from lesson 5.
Done when:grep -ci "irreversível" AGENTS.md → a number greater than 0, and each action on the list appears in a deny, in the guard, or in a porta.py pedir.
08 · Lesson quiz
Question 1 of 4
Why does the approval store the hash of the content?skill
Correct
You approve what you read. If someone (or the agent) changes the text afterward, executar refuses: "o conteúdo mudou depois da aprovação" (the content changed after approval).
Not this one
You approve what you read. If someone (or the agent) changes the text afterward, executar refuses: "o conteúdo mudou depois da aprovação" (the content changed after approval).Review: The gate to the irreversible
Question 2 of 4
Is the tty required on aprovar the security boundary?skill
Correct
A pseudo-terminal fakes a tty (the rehearsal uses script). What holds is that the agent can't write the approval, because the folder is outside its sandbox.
Not this one
A pseudo-terminal fakes a tty (the rehearsal uses script). What holds is that the agent can't write the approval, because the folder is outside its sandbox.Review: The gate to the irreversible
Question 3 of 4
The loop moves to systemd. What needs to go with it?
Correct
Outside the terminal, you are not watching. The blast radius has to fit within the cap, the sandbox and the gate, not within your session.