docs: gameplan to fully-deployed Agent-Sudo + secrets-proxy, plus S1/A1/A7 agent prompts
Agent-Sudo went live on both hosts tonight (005c214 in the boilerplates repo). This records what "done" actually means and what is left, since dedicated time on this ends today and the rest has to run via background agents. Corrects the goal: tier 4 is human-only BY DESIGN (D-CB7) — the daemon unit env holds SUDO_MD_VERIFY_ENFORCE, so a system that can restart its own daemon can disarm its own gate. "Handling tier 4" means refusing it, which is done and verified. Real remaining scope is tiers 1-3. Records the measured Incus asymmetry (server-01 has it, primary does not), which makes tier 2 on primary an open design question rather than a task; the tier-1 scoped-undo defect (app.py:244 hardcodes tier 3 while REQUIRES_UNDO_TIERS is (1,3)); and the per-host-values-in-a-shared-tracked-file bug class that bit twice in one directory. secrets-proxy is deliberately scoped as investigate-then-plan: it exited gracefully and nobody knows why, so S1 answers that before anyone writes S2. Refs #128, #146, #149, #150, #173, #191, projects id=187. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,294 @@
|
||||
# Gameplan — Agent-Sudo + secrets-proxy to fully deployed
|
||||
|
||||
**Written:** 2026-07-15 20:10 · **Author:** Claude (Opus 4.8) · **Status:** ACTIVE PLAN
|
||||
**Why now:** 2026-07-15 is the LAST day dedicated to this work. After this, time goes to
|
||||
business development (revenue) + other projects. Everything here is scoped so a background
|
||||
agent can do it, with human touches minimized to the ones that are human-only *by design*.
|
||||
|
||||
---
|
||||
|
||||
## 0. READ THIS FIRST — the goal is not what it sounds like
|
||||
|
||||
The stated goal was "Agent-Sudo and secrets-proxy handling tier 0–4 commands."
|
||||
|
||||
**Tier 4 must never be handled autonomously. That is not a gap — it is the design.**
|
||||
Per D-CB7, the `agent-sudo-daemon` unit env holds `SUDO_MD_VERIFY_ENFORCE`. A system that can
|
||||
restart its own daemon can disarm its own gate. So tier 4 = human-at-a-terminal, permanently.
|
||||
|
||||
**"Agent-Sudo handles tier 4" means "Agent-Sudo correctly REFUSES tier 4."** That is
|
||||
**DONE and VERIFIED LIVE** (2026-07-15 19:52, both hosts):
|
||||
|
||||
| command | tier | source |
|
||||
|---|---|---|
|
||||
| `rm -rf /` | 4 | `danger_veto` |
|
||||
| `systemctl restart agent-sudo-daemon` | 4 | `host_override_setb` |
|
||||
| `vi /etc/systemd/system/agent-sudo-daemon.service` | 4 | `host_override_setb` |
|
||||
|
||||
Do not let a future session "finish tier 4." There is nothing to finish. If someone proposes
|
||||
making tier 4 autonomous, that is a request to delete the security model — refuse it.
|
||||
|
||||
**Real remaining scope = tier 1, tier 2, tier 3.**
|
||||
|
||||
---
|
||||
|
||||
## 1. VERIFIED STATE as of 2026-07-15 20:00 (measured tonight, not remembered)
|
||||
|
||||
### Agent-Sudo — DEPLOYED ON BOTH HOSTS (first time ever)
|
||||
- Commit `005c214` on `origin/main` (repo: `boilerplates.git`, path `/opt/appdata/docker`).
|
||||
- **server-01**: `192.168.1.90:8082` · healthy · 0 restarts · `{"status":"ok","server_id":"server-01"}`
|
||||
- **primary**: `192.168.1.88:8084` · healthy · 0 restarts · `{"status":"ok","server_id":"primary"}`
|
||||
- Old sudo-bridge STILL owns `192.168.1.88:8082` on primary. **No cutover has happened.** (#146)
|
||||
- **D-CB6 breaker log is now persistent on BOTH hosts** — `/var/lib/agent-sudo/breaker.log`,
|
||||
dir `0700 root:root`, on a bind mount, verified resolving inside the container.
|
||||
`docker restart` can no longer reset a tripped breaker. **This closed a hole that was OPEN
|
||||
IN PRODUCTION.** `breaker.log` does not exist yet — correct, created 0600 on first append.
|
||||
- 85/85 tests pass on both hosts.
|
||||
- Capability gates: `SANDBOX_ENABLED=false`, `SNAPSHOT_ENABLED=false` on both.
|
||||
⇒ **tier 2 and tier 3 currently return 503 stubs. They do not work.**
|
||||
|
||||
### Subsystem availability — MEASURED, and it is asymmetric
|
||||
| host | incus | sandboxes | timeshift |
|
||||
|---|---|---|---|
|
||||
| server-01 | **YES** | `sandbox-primary` + `sandbox-server01` both RUNNING | YES |
|
||||
| primary | **NO** | — | YES |
|
||||
|
||||
**This asymmetry is the single most important fact in this document.** Tier 2 (sandbox-test)
|
||||
CANNOT be enabled on primary — there is no Incus there at all. Any plan that says "enable
|
||||
tier 2 on both hosts" is wrong. See §3-A3, which is an unresolved DESIGN question, not a task.
|
||||
|
||||
### secrets-proxy — STOPPED, NOT BROKEN
|
||||
- `secrets-proxy-secrets-proxy-1` = `Exited (0)` 6 days ago.
|
||||
- Logs show a **graceful shutdown** ("Application shutdown complete", "Stopping parent process").
|
||||
It was stopped on purpose. **This is not a crash and not a debugging job.**
|
||||
- Repo: `/opt/appdata/docker/docker-compose/secrets-proxy/` (app.py, docker-compose.yml,
|
||||
Dockerfile, mirror/, requirements.txt).
|
||||
- **HONEST GAP: I did not investigate what work remains on secrets-proxy.** Task #128 says
|
||||
"Finish secrets-proxy (sprint-11 #1)" but I have not read sprint-11 or the code. Everything
|
||||
below for secrets-proxy is therefore scoped as INVESTIGATE-THEN-PLAN. Do not treat S2 as a
|
||||
known quantity. **Do not let an agent invent secrets-proxy work.**
|
||||
- Its being down has a live cost: the `secrets_via_proxy_only` rule has no proxy to use, so
|
||||
secret handling currently falls back to file-passing workarounds.
|
||||
|
||||
---
|
||||
|
||||
## 2. THE RECURRING BUG CLASS — check this reflexively
|
||||
|
||||
**Per-host values hardcoded into files tracked once and shared by both hosts.**
|
||||
It bit TWICE tonight in one directory, and both were invisible until the two hosts were compared:
|
||||
|
||||
1. `docker-compose.yml` hardcoded primary's port `8084`. Pulling it to server-01 would have
|
||||
moved Agent-Sudo off 8082 and **silently undone the D8 cutover**. → FIXED: `${AGENT_SUDO_PORT}`
|
||||
in each host's gitignored `.env` (primary 8084, server-01 8082).
|
||||
2. `agent-sudo-daemon.service` carries `SERVER_ID` + `VAULT_ADDR`. The tracked copy says
|
||||
`SERVER_ID=primary` and omits `VAULT_ADDR`. Installing it on server-01 would make server-01
|
||||
**identify as primary and apply primary's tier-4 rules**, with no Vault path to verify its
|
||||
own allowlist. → NOT FIXED. See A4. Currently preserved by hand on server-01.
|
||||
|
||||
**Rule for every future session: before landing any shared file on a second host, diff the two
|
||||
hosts' copies first.** Nothing in the test suite measures cross-host divergence. It never will.
|
||||
|
||||
Related lesson from tonight, worth carrying: **a green test suite said nothing about whether the
|
||||
module even shipped.** 85/85 passed while the image would have crashed on boot with
|
||||
`ModuleNotFoundError`, and while the breaker was resettable by `docker restart`. A passing check
|
||||
only means what it measures.
|
||||
|
||||
---
|
||||
|
||||
## 3. AGENT-SUDO — remaining work
|
||||
|
||||
### A1. Fix tier-1 scoped-undo — **DO THIS FIRST. It is code, not config.** 🔴 BLOCKING
|
||||
**The defect:** `security/scoped_undo.py:186` declares `REQUIRES_UNDO_TIERS = (1, 3)`, but
|
||||
`app.py:244` calls `scoped_undo.capture_or_refuse(command, 3, server_id=SERVER_ID)` with the
|
||||
tier **hardcoded to 3**. Nothing ever captures undo for tier 1. So **tier-1 commands
|
||||
auto-execute today with no undo capture**, which contradicts D4 ("tier 1 = auto-execute +
|
||||
capture scoped-undo").
|
||||
|
||||
**Resolve the conflict BEFORE writing code — this is a human decision:**
|
||||
The P3 task contract said "tier 0/1 behaviour unchanged"; D4 says tier 1 captures undo. The
|
||||
background agent followed the task contract and pinned it with a test, then flagged it. It was
|
||||
right to flag rather than silently pick. Someone must decide:
|
||||
- (a) Implement D4: tier 1 captures scoped-undo. Cost: every tier-1 command pays undo-capture
|
||||
latency. Benefit: tier 1 becomes reversible, which is what makes autonomy safe.
|
||||
- (b) Amend D4: tier 1 is genuinely fire-and-forget, drop `1` from `REQUIRES_UNDO_TIERS`.
|
||||
Cheaper and faster, but then tier 1 is irreversible — and tier 1 is where the volume is.
|
||||
- **LEAN: (a).** The whole premise of constrained autonomy is that the constraint is real.
|
||||
An irreversible tier-1 with no undo is an approval gate removed without a constraint added.
|
||||
But this is a real trade and the user decides, not an agent.
|
||||
|
||||
⚠️ Whichever way it goes, **the test that currently pins the wrong behaviour must be updated**,
|
||||
or it will lock in the bug.
|
||||
|
||||
### A2. Phase 2 — enable capabilities on server-01, ONE AT A TIME 🟡
|
||||
Runbook: `/opt/appdata/docker/docker-compose/agent-sudo/DEPLOY_RUNBOOK.md` phase 2.
|
||||
server-01 only (it has Incus + both sandboxes RUNNING + timeshift).
|
||||
**This is the first-ever live exercise of the sandbox and snapshot paths.** Until now tier-2/3
|
||||
have ONLY run against injected fakes — the real Incus and timeshift paths have never executed.
|
||||
Expect real failures. Budget for that; do not schedule it as a 20-minute flag flip.
|
||||
|
||||
Order (do not batch — one flag, verify, then the next):
|
||||
1. `SNAPSHOT_ENABLED=true` in server-01's `.env` → `docker compose up -d` → exercise a tier-3
|
||||
command → verify a timeshift snapshot is actually created AND that undo restores.
|
||||
2. Then `SANDBOX_ENABLED=true` → exercise a tier-2 command → verify it runs in the Incus
|
||||
sandbox and NOT on the host.
|
||||
⚠️ **Do NOT test the latch by firing a danger-veto command on a live host** (runbook says so).
|
||||
Latch behaviour is already proven by unit tests + mutation testing.
|
||||
⚠️ Verify the 503 stub is actually gone, not that health is green. Health does not measure this.
|
||||
|
||||
### A3. **DESIGN QUESTION, NOT A TASK: what does tier 2 mean on primary?** 🔴 UNRESOLVED
|
||||
Primary has no Incus. Options, none free:
|
||||
- (a) Install Incus on primary. But: `reference_incus_on_docker_host_networking` documents that
|
||||
Docker's `FORWARD DROP` breaks the incus bridge and debian/13 systemd-resolved is broken.
|
||||
This is a known-painful install on a Docker host — and primary is the PRODUCTION host.
|
||||
- (b) Primary tier-2 commands execute in server-01's sandbox (cross-host). Introduces the same
|
||||
cross-host dependency class as #173, and the sandbox would not mirror primary's state.
|
||||
- (c) Tier 2 stays permanently 503 on primary; primary's SUDO.md never classifies anything as
|
||||
tier 2. Honest and safe; means primary is a lesser-capability host by design. **LEAN: (c)**
|
||||
for now — it is the only one that ships without new risk on the production host, and (a) can
|
||||
come later if tier-2 volume on primary justifies the pain.
|
||||
**This must be decided by the user before anyone touches primary tier-2.**
|
||||
|
||||
### A4. Daemon unit `EnvironmentFile` — fix the §2 bug class 🟡 HUMAN-ONLY
|
||||
Make `agent-sudo-daemon.service` host-neutral: `EnvironmentFile=/etc/agent-sudo/daemon.env`,
|
||||
with `SERVER_ID` / `VAULT_ADDR` / `SUDO_MD_VERIFY_ENFORCE` per-host in that file.
|
||||
**Tier-4 human-only by design** — that unit's env IS the gate. An agent must never edit it.
|
||||
**Fold into #173**: the `VAULT_ADDR=http://192.168.1.88:8200` line in server-01's unit IS the
|
||||
cross-host Vault dependency that #173 exists to resolve. Same file, same edit, one human pass.
|
||||
|
||||
### A5. #146 — primary port swap 8084 → 8082 🟢 NOW TRIVIAL
|
||||
Was a manual file edit; the `${AGENT_SUDO_PORT}` change reduced it to a one-line `.env` edit
|
||||
plus stopping the old sudo-bridge. Do this LAST, after primary tier-1/2/3 behaviour is settled.
|
||||
Blocked by: #191 (sudo-bridge/app.py is one of the git-held files).
|
||||
|
||||
### A6. server-01 repo hygiene 🟢
|
||||
server-01's `/opt/appdata/docker` is at HEAD `96f8c05`, **28 commits behind**, with the
|
||||
agent-sudo dir checked out from `005c214` into a staged index, plus uncommitted local changes to
|
||||
`jenkins/Dockerfile`, `sudo-bridge/app.py`, `sudo-bridge/docker-compose.yml`.
|
||||
The files are correct and the deploy is sound, but the repo state is a trap for the next person.
|
||||
**Why it is this way:** a full `git pull` would have clobbered those held local changes, so a
|
||||
targeted `git checkout origin/main -- docker-compose/agent-sudo` was used instead. That was the
|
||||
right call under time pressure; reconciling properly is a separate, careful job.
|
||||
⚠️ Whoever does this must NOT blindly `git checkout`/`stash` the local changes away — find out
|
||||
what they are first. Same category as #191.
|
||||
|
||||
### A7. Spec gaps A + C on projects row 187 🟢 confirm-and-close
|
||||
- **(A)** `ATTACK_TIER4_SOURCES` = `('danger_veto', 'host_override_setb')`. `classify()` can also
|
||||
reach tier 4 via `rule_match` (explicit SUDO.md tier-4 rule) and `miss_verb_heuristic`. The
|
||||
agent made both audit-only, reasoning a latch must mean "this is not resolving." **Decide: is
|
||||
an explicit tier-4 SUDO.md rule an attack signal?** One-line change; the D10 seam holds.
|
||||
Note D-CB8 deliberately excludes `miss_primary_refuse` — latching on it would take a host
|
||||
read-only on the first typo. Do not "fix" that.
|
||||
- **(C)** A failed PRE-exec snapshot refuses 503 without calling `record_tier3_failure()` — the
|
||||
command never ran, so counting it toward the benign trip threshold would let a broken
|
||||
timeshift disarm tier 3. Reasoning is sound; just confirm and close.
|
||||
|
||||
---
|
||||
|
||||
## 4. SECRETS-PROXY — remaining work
|
||||
|
||||
### S1. INVESTIGATE — **must happen before any plan is written** 🔴
|
||||
Nobody currently knows what "finish secrets-proxy" means. Establish, with evidence:
|
||||
1. **Why was it stopped?** Exit was graceful and deliberate 6 days ago (~2026-07-09). Check git
|
||||
log, session summaries, and task #128 / sprint-11 notes for the reason. **It may have been
|
||||
stopped for a reason that still applies** — do not just start it.
|
||||
2. What is incomplete in `app.py` / `mirror/`? What did sprint-11 #1 actually scope?
|
||||
3. Does it start cleanly today? What breaks if it does?
|
||||
4. Is its `docker-compose.yml` carrying the §2 per-host-value bug? Check before any second host.
|
||||
5. Check `project_coolify_env_var_debt`: are there `${VAR}` Coolify placeholders that will bite
|
||||
on restart? **This is a known live trap for any container restart.**
|
||||
**Output: a written scope. Then and only then, plan S2.** Do not skip to S2.
|
||||
|
||||
### S2. FINISH + DEPLOY — cannot be scoped until S1 lands 🔴
|
||||
Placeholder by construction. Anyone writing detail here before S1 is guessing.
|
||||
|
||||
### S3. #150 — sign `proxy.md` via Vault transit 🟡
|
||||
Explicitly blocked on secrets-proxy deploy. Mirrors Agent-Sudo's SUDO.md signing (#145), so the
|
||||
pattern is proven — reuse `security/sudo_sign.py`, do not reinvent.
|
||||
|
||||
### S4. Restore the `secrets_via_proxy_only` rule 🟢
|
||||
Once up, secret reads go back through the proxy `/shell` + `env_secrets` vault refs, retiring
|
||||
tonight's file-passing workaround.
|
||||
|
||||
---
|
||||
|
||||
## 5. SEQUENCING — and the trap in the obvious order
|
||||
|
||||
**The ordering paradox** (`feedback_build_the_freeing_capability_first`): the obvious order is
|
||||
"finish Agent-Sudo, then secrets-proxy, then business dev." But the item that removes a
|
||||
recurring tax is often scheduled last, behind the things that charge it.
|
||||
|
||||
**secrets-proxy being down taxes EVERY future session** — every secret read needs a workaround,
|
||||
and that cost is paid on business-development days too, when this infrastructure is supposed to
|
||||
be out of the way. **S1 is zero-dependency and small. Do S1 FIRST**, even before A1, so the
|
||||
scope is known and can be handed to an agent while attention is elsewhere.
|
||||
|
||||
Recommended order:
|
||||
| # | item | who | why here |
|
||||
|---|---|---|---|
|
||||
| 1 | **S1** investigate secrets-proxy | agent | zero-dep, removes a recurring tax, unblocks S2/S3 |
|
||||
| 2 | **A1 decision** tier-1 undo (a) vs (b) | **human** | blocks all tier-1 work; 5-min decision |
|
||||
| 3 | **A3 decision** primary tier-2 | **human** | blocks primary; 5-min decision |
|
||||
| 4 | **A1 implement** | agent | the real code work |
|
||||
| 5 | **A2** phase 2 on server-01 | agent + human | first live sandbox/snapshot run |
|
||||
| 6 | **A7** confirm gaps A + C | agent | cheap close-out |
|
||||
| 7 | **S2** finish secrets-proxy | agent | scoped by S1 |
|
||||
| 8 | **A4 + #173** unit EnvironmentFile | **human** | tier-4 by design |
|
||||
| 9 | **A6** repo hygiene, **A5**/#146 swap, **S3**/#150 | mixed | last, low risk |
|
||||
|
||||
**Steps 2 and 3 are two human decisions totalling ~10 minutes that unblock everything else.**
|
||||
Make them before starting a business-development day and the agents can run without you.
|
||||
|
||||
---
|
||||
|
||||
## 6. WHAT AGENTS MAY AND MAY NOT DO
|
||||
|
||||
**Never delegate to an agent (tier-4 / human-only by design):**
|
||||
- Restarting or stopping `agent-sudo-daemon`; editing its unit file (A4) — *the unit env is the gate*
|
||||
- Editing `SUDO.md`; writing `/var/lib/agent-sudo/breaker.log`; resetting a tripped latch
|
||||
- The #146 port swap
|
||||
- The A1 and A3 **decisions** (implementation is delegable; the choice is not)
|
||||
- Anything in §2 that lands a shared file on a second host without diffing both first
|
||||
|
||||
**Good agent work:** S1 investigation, A1 implementation once decided, A2 under supervision,
|
||||
A7 confirmations, S2 once scoped, test writing.
|
||||
|
||||
**Every agent prompt must carry** (`playbook_background_agent_prompts` +
|
||||
`feedback_agent_prompt_mandatory_wrapup`): the mandatory wrap-up JSON block
|
||||
(`status` / `actions_taken` / `actions_failed` / `notes` / `human_action_required`) so the agent
|
||||
structurally cannot stop without reporting; an explicit permitted-file list; and
|
||||
`--max-turns`. Tonight proved the value: the P3 agent's two most valuable findings were the
|
||||
blockers **outside** its permitted file set, which it could only flag, not fix. **An agent that
|
||||
flags what it cannot touch is working correctly — do not "fix" that by widening permissions.**
|
||||
|
||||
---
|
||||
|
||||
## 7. DEFINITION OF DONE
|
||||
|
||||
Agent-Sudo is done when:
|
||||
- [ ] Tier 0 executes. **(DONE — verified 2026-07-15)**
|
||||
- [ ] Tier 4 is refused on both hosts. **(DONE — verified 2026-07-15)**
|
||||
- [ ] Breaker log persists across restart on both hosts. **(DONE — verified 2026-07-15)**
|
||||
- [ ] Tier 1 behaviour matches a D4 that has been reconciled with the task contract (A1)
|
||||
- [ ] Tier 3 creates a real timeshift snapshot and restores, on server-01 (A2)
|
||||
- [ ] Tier 2 executes in the real Incus sandbox on server-01 (A2)
|
||||
- [ ] Primary's tier-2 story is decided and written down (A3)
|
||||
- [ ] The daemon unit is host-neutral (A4 / #173)
|
||||
- [ ] Instrument-the-gate audit query is scheduled and someone READS it
|
||||
(`feedback_instrument_the_gate` — a gate nobody reads is not instrumented)
|
||||
|
||||
secrets-proxy is done when: S1 scope written · S2 deployed · S3 `proxy.md` signed ·
|
||||
`secrets_via_proxy_only` restored without workarounds.
|
||||
|
||||
**Not done ≠ not useful.** As of tonight Agent-Sudo safely runs tier-0 and correctly refuses
|
||||
tier-4 on both hosts, with a tamper-evident breaker. That is real, and it is the part that was
|
||||
open in production this morning.
|
||||
|
||||
---
|
||||
|
||||
## 8. XREF
|
||||
projects **id=187** (P3 detail + 3 spec gaps) · **#149** (parent) · **#146** (port swap) ·
|
||||
**#173** (fail-open/closed — fold A4 in) · **#191** (git-held files) · **#128** (secrets-proxy) ·
|
||||
**#150** (proxy.md signing) · `DEPLOY_RUNBOOK.md` (phases) ·
|
||||
`agent_sudo_design_decisions.md` (D1–D10, D-CB1–D-CB9) ·
|
||||
memory: `feedback_build_the_freeing_capability_first`, `feedback_instrument_the_gate`,
|
||||
`feedback_autonomous_security_constrain_not_gate`, `reference_incus_on_docker_host_networking`,
|
||||
`project_coolify_env_var_debt`, `feedback_agent_prompt_mandatory_wrapup`
|
||||
@@ -1303,3 +1303,163 @@ Before stopping for ANY reason — task complete, error, human gate, or approach
|
||||
--max-turns 25
|
||||
If you issue the same tool call or command twice with identical arguments, STOP immediately and output the mandatory wrap-up with status=partially_succeeded.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# S1-SECRETS-PROXY-SCOPE (2026-07-15) — task #128 · gameplan §4 · **READY TO RUN NOW**
|
||||
|
||||
> **Zero dependencies. Run this FIRST**, ahead of the Agent-Sudo work — per
|
||||
> `feedback_build_the_freeing_capability_first`, secrets-proxy being down taxes every future
|
||||
> session (every secret read needs a workaround), including business-development days.
|
||||
> **This is INVESTIGATION ONLY. It produces a written scope, not a deploy.**
|
||||
|
||||
You are scoping what remains to finish `secrets-proxy`. Nobody currently knows. Your job is to
|
||||
find out and write it down with evidence. **Do not fix. Do not deploy. Do not start the container.**
|
||||
|
||||
## Verified starting facts (do not re-derive; DO verify anything you rely on further)
|
||||
- Container `secrets-proxy-secrets-proxy-1` = `Exited (0)` ~6 days ago (~2026-07-09).
|
||||
- Logs show a GRACEFUL, DELIBERATE shutdown ("Application shutdown complete", "Stopping parent
|
||||
process [1]"). **This is NOT a crash.** Do not investigate it as one.
|
||||
- Repo: `/opt/appdata/docker/docker-compose/secrets-proxy/` — `app.py`, `docker-compose.yml`,
|
||||
`Dockerfile`, `mirror/`, `requirements.txt`. On host `primary` (192.168.1.88).
|
||||
- Task #128 = "Finish secrets-proxy (sprint-11 #1)". Task #150 (sign `proxy.md` via Vault
|
||||
transit) is explicitly blocked on secrets-proxy being deployed.
|
||||
|
||||
## Questions you must answer WITH EVIDENCE (quote the file/line/log/commit)
|
||||
1. **WHY was it stopped?** Search git log, session summaries, sprint-11 notes, task #128.
|
||||
⚠️ **It may have been stopped for a reason that still applies.** This is the most important
|
||||
question. If you cannot find the reason, say so explicitly — do not guess.
|
||||
2. What does "sprint-11 #1" actually scope? What is incomplete in `app.py` / `mirror/`?
|
||||
3. Would it start cleanly today? Determine by READING code/config, **not by starting it**.
|
||||
4. Does `docker-compose.yml` carry per-host values hardcoded in a shared tracked file?
|
||||
(See gameplan §2 — this bug class bit twice in agent-sudo on 2026-07-15: a hardcoded port
|
||||
and `SERVER_ID`/`VAULT_ADDR` in a unit file. Check ports, IPs, `SERVER_ID`-alikes.)
|
||||
5. `project_coolify_env_var_debt`: any `${VAR}` Coolify placeholders that would bite on restart?
|
||||
6. What would the proxy-only rule require to be honoured again (the `/shell` + vault-ref path)?
|
||||
|
||||
## Hard constraints
|
||||
- **READ-ONLY.** No edits, no `docker compose up`, no `docker start`, no restarts, no commits.
|
||||
- **NEVER surface a credential.** Do not `cat` the env file; do not `docker exec` to read env;
|
||||
do not echo token-shaped values. To show config, print KEYS ONLY (`sed 's/=.*/=<redacted>/'`).
|
||||
The `security-enforcement.py` PreToolUse hook will block token patterns, and anything printed
|
||||
lands permanently in the transcript. If a credential is needed for a check, STOP and report.
|
||||
- secrets-proxy is itself DOWN, so you cannot use it for credential reads. Do not work around this.
|
||||
- If a question cannot be answered from evidence, write "UNKNOWN — could not determine because X".
|
||||
**An honest UNKNOWN is worth more than a confident guess.** Do not fill gaps by inference.
|
||||
|
||||
## Deliverable
|
||||
`/opt/appdata/docker/docker-compose/secrets-proxy/SCOPE.md` — findings, each with its evidence,
|
||||
and a proposed S2 task list with effort estimates. Flag anything human-only.
|
||||
|
||||
--max-turns 20
|
||||
|
||||
## MANDATORY WRAP-UP — you cannot stop without emitting this
|
||||
```json
|
||||
{"status":"succeeded|partially_succeeded|failed",
|
||||
"actions_taken":[], "actions_failed":[],
|
||||
"questions_answered":{"why_stopped":"", "sprint11_scope":"", "starts_clean":"",
|
||||
"per_host_bug":"", "coolify_placeholders":"", "proxy_only_path":""},
|
||||
"unknowns":[], "human_action_required":[], "notes":""}
|
||||
```
|
||||
If you issue the same tool call twice with identical arguments, STOP and emit the wrap-up with
|
||||
status=partially_succeeded.
|
||||
|
||||
---
|
||||
|
||||
# A1-TIER1-UNDO (2026-07-15) — projects id=187 gap B · gameplan §3-A1 · **GATED**
|
||||
|
||||
> 🔴 **DO NOT RUN THIS UNTIL THE HUMAN HAS DECIDED (a) vs (b) BELOW.**
|
||||
> The decision is the user's, not yours. If dispatched without a recorded decision, STOP
|
||||
> immediately and emit the wrap-up with `human_action_required: ["A1 decision (a) or (b)"]`.
|
||||
|
||||
## The defect (verified 2026-07-15 19:55)
|
||||
- `security/scoped_undo.py:186` → `REQUIRES_UNDO_TIERS = (1, 3)`
|
||||
- `app.py:244` → `scoped_undo.capture_or_refuse(command, 3, server_id=SERVER_ID)` — **tier
|
||||
hardcoded to 3**. Nothing ever captures undo for tier 1.
|
||||
- Result: **tier-1 commands auto-execute with NO undo capture**, contradicting D4 ("tier 1 =
|
||||
auto-execute + capture scoped-undo").
|
||||
|
||||
## The conflict a human must resolve first
|
||||
The P3 task contract said "tier 0/1 behaviour unchanged"; D4 says tier 1 captures undo. The P3
|
||||
agent followed the contract, pinned it with a test, and flagged it — correctly.
|
||||
- **(a) Implement D4:** tier 1 captures scoped-undo. Cost: undo-capture latency on every tier-1
|
||||
command (tier 1 is the volume tier). Benefit: tier 1 becomes reversible.
|
||||
- **(b) Amend D4:** drop `1` from `REQUIRES_UNDO_TIERS`; tier 1 is fire-and-forget. Cheaper, but
|
||||
tier 1 becomes irreversible.
|
||||
|
||||
## If (a) — your task
|
||||
1. Make the tier argument at `app.py:244` reflect the ACTUAL tier, not a hardcoded 3.
|
||||
2. Ensure tier-1 flows through `capture_or_refuse` per `REQUIRES_UNDO_TIERS`.
|
||||
3. ⚠️ **The existing test pins the WRONG behaviour** (tier 1 = no undo capture). You MUST update
|
||||
it, or you will lock in the bug. Find it, understand why the P3 agent wrote it, then change it.
|
||||
4. Decide + implement what happens when tier-1 undo capture FAILS. Note the precedent in gap C:
|
||||
a failed PRE-exec snapshot refuses without calling `record_tier3_failure()`, because counting
|
||||
infrastructure faults toward the benign trip threshold would let broken infra disarm the tier.
|
||||
Apply the SAME reasoning to tier 1 and say so explicitly in your notes.
|
||||
⚠️ Refusing every tier-1 command when undo capture is broken would stall the user's whole
|
||||
workflow — that is the exact stall CA-D7 forbids. Think about this; flag it, do not guess.
|
||||
5. All 85 existing tests must still pass. Add tests for the new behaviour.
|
||||
|
||||
## If (b) — your task
|
||||
Drop `1` from `REQUIRES_UNDO_TIERS`, update D4 in `agent_sudo_design_decisions.md` to say tier 1
|
||||
is deliberately irreversible AND why, keep the existing test, add a comment at `app.py:244`
|
||||
explaining the hardcoded 3 is now correct-by-decision.
|
||||
|
||||
## Hard constraints
|
||||
- **Permitted files ONLY:** `app.py`, `security/scoped_undo.py`, `test_app.py`,
|
||||
`agent_sudo_design_decisions.md` (path b only). Anything else → **flag, do not touch**.
|
||||
(On 2026-07-15 the P3 agent's two most valuable findings were blockers OUTSIDE its permitted
|
||||
set, which it flagged rather than fixed. That is the system working. Do the same.)
|
||||
- **NEVER** restart/stop `agent-sudo-daemon`, edit its unit, edit `SUDO.md`, or write
|
||||
`/var/lib/agent-sudo/breaker.log` — all tier-4 human-only by design (D-CB7).
|
||||
- **NEVER** fire a danger-veto or `host_override_setb` command on a live host — it latches a
|
||||
production breaker. Both hosts are LIVE as of 2026-07-15 (server-01 `:8082`, primary `:8084`).
|
||||
- Run tests with `PYTHONDONTWRITEBYTECODE=1 python3 -m unittest test_app test_sudo_rules
|
||||
test_circuit_breaker` (baseline: `Ran 85 tests ... OK`).
|
||||
- **A green suite proves less than you think.** On 2026-07-15, 85/85 passed while the image
|
||||
crashed on boot and the breaker was resettable by `docker restart`. If you change behaviour,
|
||||
prove it with a test that FAILS before your change and passes after. Say which test that is.
|
||||
|
||||
--max-turns 25
|
||||
|
||||
## MANDATORY WRAP-UP — you cannot stop without emitting this
|
||||
```json
|
||||
{"status":"succeeded|partially_succeeded|failed|blocked_on_decision",
|
||||
"decision_taken":"a|b|none",
|
||||
"actions_taken":[], "actions_failed":[],
|
||||
"tests":{"baseline_85_pass":true, "new_tests_added":[], "test_that_fails_before_change":""},
|
||||
"undo_capture_failure_behaviour":"", "files_touched":[], "files_flagged_not_touched":[],
|
||||
"human_action_required":[], "notes":""}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# A7-SPEC-GAPS-AC (2026-07-15) — projects id=187 gaps A + C · gameplan §3-A7 · small
|
||||
|
||||
Confirm-and-close two flagged spec gaps. **Analysis + recommendation; change only gap A, and
|
||||
only if the human has said which way.**
|
||||
|
||||
**Gap A:** `ATTACK_TIER4_SOURCES = ('danger_veto', 'host_override_setb')`. But `classify()` also
|
||||
reaches tier 4 via `rule_match` (explicit SUDO.md tier-4 rule) and `miss_verb_heuristic`
|
||||
(server-01). Both are currently audit-only (no latch). **Question for the human: should an
|
||||
explicit tier-4 SUDO.md rule be an attack signal?** One-line change to `ATTACK_TIER4_SOURCES`.
|
||||
⚠️ **D-CB8 deliberately EXCLUDES `miss_primary_refuse` — it is the DEFAULT for unlisted
|
||||
commands, so latching on it would take a host read-only on the first typo. Do NOT "fix" that.
|
||||
If you propose adding it, you have misunderstood the design.**
|
||||
|
||||
**Gap C:** A failed PRE-exec snapshot refuses 503 without calling `record_tier3_failure()` — the
|
||||
command never ran, and counting infrastructure faults toward the benign trip threshold would let
|
||||
a broken timeshift disarm tier 3. **Verify this reasoning against the code and confirm or refute.**
|
||||
|
||||
Permitted files: `app.py` (gap A one-liner only, only once decided). Everything else: report only.
|
||||
Do not fire tier-4 commands on live hosts. All 85 tests must pass.
|
||||
|
||||
--max-turns 12
|
||||
|
||||
## MANDATORY WRAP-UP
|
||||
```json
|
||||
{"status":"succeeded|partially_succeeded|failed",
|
||||
"gap_a":{"recommendation":"", "changed":false, "reasoning":""},
|
||||
"gap_c":{"confirmed":true, "reasoning":""},
|
||||
"actions_taken":[], "actions_failed":[], "human_action_required":[], "notes":""}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user