Productionizes the erp-sandbox state-lifecycle mechanisms from ADR-0003, validated live against the running sandbox. Adds ops/sandbox/sandbox-lifecycle.sh + README.
refresh-from-prod — read-only pg_dump of prod erp → DROP OWNED BY erp_sandbox_role CASCADE → pg_restore into erp-sandbox, with the sandbox's own membership creds (no DROP/CREATE DATABASE, no CREATEDB, no superuser). Dumps the fullpublic schema (so app helper functions + triggers like update_modified_column_tms() come over) and filters the provisioner-owned pgbouncer user_lookup from the restore TOC. Scales the pod to 0 for exclusive access; prod creds are copied into a transient secret deleted on exit.
sync-documents — tar-pipes documents/mycompany (company logo + uploads) prod → sandbox, since files live on the PVC not the DB.
Proven live
I ran both against erp-sandbox: it now carries prod's 295 llx_* tables, real data (societe/facture rows), company config (Arcodange, fr_FR, theme), MAIN_MODULE_API=1, all owned by erp_sandbox_role; the login renders identically to prod and the company logo returns HTTP 200. Prod was read-only throughout (default_transaction_read_only=on), and the restore credential is structurally unable to touch prod erp (owns only erp-sandbox).
Caveats documented (ADR-0003)
Encryption (API keys are instance-uuid-bound → ai_agent_sandbox key must be generated in-sandbox) and the PVC/file fidelity (logo image needs sync-documents). README also lists the hardening backlog: a dedicated read-only dump role + a golden-cache PVC for fast BDD resets.
## Summary
Productionizes the `erp-sandbox` state-lifecycle mechanisms from [ADR-0003](https://gitea.arcodange.lab/arcodange-org/factory/src/branch/main/vibe/ADR/0003-sandbox-state-lifecycle.md), **validated live** against the running sandbox. Adds `ops/sandbox/sandbox-lifecycle.sh` + README.
- **`refresh-from-prod`** — read-only `pg_dump` of prod `erp` → `DROP OWNED BY erp_sandbox_role CASCADE` → `pg_restore` into `erp-sandbox`, with the sandbox's own membership creds (no `DROP/CREATE DATABASE`, no `CREATEDB`, no superuser). Dumps the **full** `public` schema (so app helper functions + triggers like `update_modified_column_tms()` come over) and filters the provisioner-owned pgbouncer `user_lookup` from the restore TOC. Scales the pod to 0 for exclusive access; prod creds are copied into a transient secret deleted on exit.
- **`sync-documents`** — tar-pipes `documents/mycompany` (company logo + uploads) prod → sandbox, since files live on the PVC not the DB.
### Proven live
I ran both against `erp-sandbox`: it now carries prod's 295 `llx_*` tables, real data (societe/facture rows), company config (Arcodange, `fr_FR`, theme), `MAIN_MODULE_API=1`, all owned by `erp_sandbox_role`; the login renders identically to prod and the company logo returns HTTP 200. Prod was read-only throughout (`default_transaction_read_only=on`), and the restore credential is structurally unable to touch prod `erp` (owns only `erp-sandbox`).
### Caveats documented (ADR-0003)
Encryption (API keys are instance-uuid-bound → `ai_agent_sandbox` key must be generated in-sandbox) and the PVC/file fidelity (logo image needs `sync-documents`). README also lists the hardening backlog: a dedicated read-only dump role + a golden-cache PVC for fast BDD resets.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Productionizes the sandbox state-lifecycle mechanisms validated live against
erp-sandbox. `ops/sandbox/sandbox-lifecycle.sh`:
- refresh-from-prod: read-only pg_dump of prod erp (default_transaction_read_only)
-> DROP OWNED BY erp_sandbox_role CASCADE -> pg_restore into erp-sandbox, using
the sandbox's own membership creds (no DROP/CREATE DATABASE, no CREATEDB, no
superuser). Dumps the full public schema (so app helper functions + triggers
come over) and filters the provisioner-owned pgbouncer user_lookup function
from the restore TOC. Scales the pod to 0 for exclusive access; copies prod
creds into a transient secret that is deleted on exit.
- sync-documents: tar-pipe the documents/mycompany tree (company logo + uploads)
prod -> sandbox, since uploaded files live on the PVC, not the DB.
Prod integrity is structural: prod is read-only during dump; the restore can only
write erp-sandbox (erp_sandbox_role owns only the sandbox DB and cannot drop prod
erp/erp_role); the platform's only prod-capable superuser stays behind the
human-gated postgres.yaml CI and is never used here.
README documents the integrity guarantee, the encryption + PVC fidelity caveats,
the BDD reset loop, and the hardening backlog (dedicated read-only dump role,
golden-cache PVC).
Refs ADR-0003 (factory#19). Chart owner-role fix = erp#13.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Productionizes the
erp-sandboxstate-lifecycle mechanisms from ADR-0003, validated live against the running sandbox. Addsops/sandbox/sandbox-lifecycle.sh+ README.refresh-from-prod— read-onlypg_dumpof proderp→DROP OWNED BY erp_sandbox_role CASCADE→pg_restoreintoerp-sandbox, with the sandbox's own membership creds (noDROP/CREATE DATABASE, noCREATEDB, no superuser). Dumps the fullpublicschema (so app helper functions + triggers likeupdate_modified_column_tms()come over) and filters the provisioner-owned pgbounceruser_lookupfrom the restore TOC. Scales the pod to 0 for exclusive access; prod creds are copied into a transient secret deleted on exit.sync-documents— tar-pipesdocuments/mycompany(company logo + uploads) prod → sandbox, since files live on the PVC not the DB.Proven live
I ran both against
erp-sandbox: it now carries prod's 295llx_*tables, real data (societe/facture rows), company config (Arcodange,fr_FR, theme),MAIN_MODULE_API=1, all owned byerp_sandbox_role; the login renders identically to prod and the company logo returns HTTP 200. Prod was read-only throughout (default_transaction_read_only=on), and the restore credential is structurally unable to touch proderp(owns onlyerp-sandbox).Caveats documented (ADR-0003)
Encryption (API keys are instance-uuid-bound →
ai_agent_sandboxkey must be generated in-sandbox) and the PVC/file fidelity (logo image needssync-documents). README also lists the hardening backlog: a dedicated read-only dump role + a golden-cache PVC for fast BDD resets.🤖 Generated with Claude Code
Productionizes the sandbox state-lifecycle mechanisms validated live against erp-sandbox. `ops/sandbox/sandbox-lifecycle.sh`: - refresh-from-prod: read-only pg_dump of prod erp (default_transaction_read_only) -> DROP OWNED BY erp_sandbox_role CASCADE -> pg_restore into erp-sandbox, using the sandbox's own membership creds (no DROP/CREATE DATABASE, no CREATEDB, no superuser). Dumps the full public schema (so app helper functions + triggers come over) and filters the provisioner-owned pgbouncer user_lookup function from the restore TOC. Scales the pod to 0 for exclusive access; copies prod creds into a transient secret that is deleted on exit. - sync-documents: tar-pipe the documents/mycompany tree (company logo + uploads) prod -> sandbox, since uploaded files live on the PVC, not the DB. Prod integrity is structural: prod is read-only during dump; the restore can only write erp-sandbox (erp_sandbox_role owns only the sandbox DB and cannot drop prod erp/erp_role); the platform's only prod-capable superuser stays behind the human-gated postgres.yaml CI and is never used here. README documents the integrity guarantee, the encryption + PVC fidelity caveats, the BDD reset loop, and the hardening backlog (dedicated read-only dump role, golden-cache PVC). Refs ADR-0003 (factory#19). Chart owner-role fix = erp#13. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>