KB-7BA2

O11 automation+agent-sandbox bundle — 03 Backup operator package

7 min read Revision 1
dieu44iu-cutterv0.6o11automation-agent-sandboxbackup-f4

O11 Report 03 — Backup operator package, finalized (BRANCH 2)

  • macro: v0.6-o11-automation-agent-sandbox-program-bundle
  • date_utc: 2026-05-21 · host: Contabo vmi3080463 · gate: BRANCH 2
  • result: PASS — F4 operator package finalized; contains NO secret values; not executed by this macro

1. Scope

Finalize the operator steps that close F4 — BACKUP_GPG_FPR (blocker B3 in Report 02). This is a secret-creation step; per the macro's forbidden list it is not performed here. This report is a complete, copy-pasteable runbook the operator executes on the VPS. No private key is created or logged here.

2. What is already done (no operator action)

backup_runner.py:   /var/lib/cutter/backup_runner.py — present, fail-closed,
                    NON-SENSITIVE (credential NAMES only). O9 F4 artifact.
mechanism proven:   gpg 2.4.4 + pg_dump 16 — ephemeral round-trip (O8E R03)
9 mutation tables:  all exist (O9 R02 §3)
adapter wiring:     ProductionLiveExecutionAdapter(backup_runner=…) accepts it
selftest entry:     python3 /var/lib/cutter/backup_runner.py --selftest

3. What is missing (the entire operator package below)

BACKUP_GPG_FPR:   absent from /opt/incomex/docker/.env  (verified this macro)
host keyring:     no backup public key imported
=> backup_runner --selftest currently FAILS CLOSED, by design:
   "required env var BACKUP_GPG_FPR is unset"

4. Operator package — provision BACKUP_GPG_FPR

Run on the VPS as the operator. Never paste the passphrase into .env, a shell history file, the KB, or any report. Store it in the operator vault.

# --- STEP 1. Generate a dedicated, passphrase-protected backup keypair --------
gpg --batch --gen-key <<'EOF'
  Key-Type: default
  Key-Length: 4096
  Subkey-Type: default
  Name-Real: dot-iu-cutter backup
  Name-Email: cutter-backup@incomexsaigoncorp.vn
  Expire-Date: 2y
  Passphrase: <OPERATOR-CHOSEN — store in the vault, NEVER in .env / KB / logs>
EOF

# --- STEP 2. Capture the 40-char fingerprint ---------------------------------
gpg --list-keys --with-colons | awk -F: '/^fpr:/{print $10; exit}'
#   -> copy this value; it is the ONLY thing that goes into .env (it is NOT a secret;
#      a fingerprint is public — but the private key and passphrase ARE secret).

# --- STEP 3. Publish the fingerprint NAME=value into the env file ------------
#   Operator edits /opt/incomex/docker/.env and appends ONE line:
#       BACKUP_GPG_FPR=<fingerprint-from-step-2>
#   (no other change; do not add the passphrase or the key)

# --- STEP 4. Ensure the backup PUBLIC key is in the keyring of the user that
#             runs the first-run command package (so gpg --encrypt resolves it) -
gpg --export --armor <fingerprint> > /root/cutter-backup-public.asc   # if needed
gpg --import /root/cutter-backup-public.asc

# --- STEP 5. Verify readiness — NO DB write, NO dump, NO secret printed ------
set -a; . /opt/incomex/docker/.env; set +a
python3 /var/lib/cutter/backup_runner.py --selftest
#   expect:  SELFTEST: PASS
#            pg_dump / gpg paths, BACKUP_GPG_FPR present (…last-8 only), DB target,
#            backups dir, 9 tables in scope.

5. Companion grant package (G1 — apply with the run, as workflow_admin)

backup_runner's pg_dump runs as least-privilege cutter_exec and must SELECT all 9 tables. Two SELECTs are still missing — apply immediately before the first run, then verify:

-- as workflow_admin on database `directus`
GRANT SELECT ON cutter_governance.verify_result              TO cutter_exec;
GRANT SELECT ON cutter_governance.cut_change_set_affected_row TO cutter_exec;

SELECT has_table_privilege('cutter_exec','cutter_governance.verify_result','SELECT'),
       has_table_privilege('cutter_exec','cutter_governance.cut_change_set_affected_row','SELECT');
-- both must return true.

6. Config-drift fix (do this in the same env step — non-secret)

file:   /var/lib/cutter/orchestrator.config.json
field:  deployed_source_commit : "cad989a7c7c37c1b042778f0b601a599a6d04ee3"
        -> "f111d4abd098bfd6653b157ea45a83e086c0a2fe"
field:  milestone_of_record    : "O8B"  ->  "O10"
note:   non-loading metadata file; this only keeps the record honest.
        Supersedes O9 R06 §1 (which named the now-stale fdcf580).

7. Verification at the first run (operator confirms)

during Phase 4 (pre_write_backup): an encrypted dump is produced at
  /var/lib/cutter/backups/<run_id>.sql.gpg
backup_runner returns a COMPLETE envelope {sha256,size_bytes,gpg_fpr,artifact_ref};
  a missing field => adapter raises StopInvariantFailed (fail-closed, run aborts).
0 production rows change as a result of the backup step.

8. Rollback / disable

disable backup provisioning (pre-first-run, no data exists):
  - remove the BACKUP_GPG_FPR line from /opt/incomex/docker/.env
  - (optional) gpg --delete-secret-keys <fpr> ; gpg --delete-keys <fpr>
  => backup_runner --selftest returns to FAIL-CLOSED; no run can pass Phase 4.
restore from a backup (catastrophic path only — GAP6 soft-revert is preferred):
  gpg --decrypt /var/lib/cutter/backups/<run_id>.sql.gpg > /tmp/<run_id>.sql
  # inspect, then restore wanted tables into a SCRATCH db or via table-rename —
  # never blind-restore over a live schema.
key rotation:
  generate a new keypair (steps 1-2), update BACKUP_GPG_FPR, re-import the
  public key. Old encrypted artifacts still decrypt with the old private key —
  retain it in the vault until those artifacts are retired.

9. Secret-handling attestation

private key created by this macro:   NO
passphrase chosen/written/logged:    NO
BACKUP_GPG_FPR value written:        NO  (operator does, in step 3)
secrets in this report:              NONE — names and procedure only

10. Verdict

operator package:   FINALIZED — copy-pasteable, no secret values
verification:       --selftest + has_table_privilege checks included
rollback/disable:   included (§8)
executed here:      NO — F4 (B3) remains an operator step
branch_2:           PASS
Back to Knowledge Hub knowledge/dev/laws/dieu44-trien-khai/v0.6-o11-automation-agent-sandbox-program-bundle/03-backup-operator-package.md