[vibe](../../README.md) > [Guidebooks](../README.md) > [ERP](README.md) > **Operations** # ERP Operations > **Status:** ✅ Active > **Last Updated:** 2026-06-23 > **Upstream:** [ERP hub](README.md) · [Deployment](deployment.md) > **Downstream:** [Backup & recovery](backup-and-recovery.md) > **Related:** [Applications hub](../applications/README.md) · [Web app](../applications/webapp.md) · [Secrets & VSO](../tools/secrets-and-vso.md) · [Postgres IaC](../factory-provisioning/opentofu/postgres-iac.md) This page covers day-2 operation of the Arcodange Dolibarr ERP: the read-only operations CLI, the static identity assets, the Playwright bootstrap test suite, and the recurring scaling / module-activation / storage chores. For how the workload is deployed onto the cluster, see [Deployment](deployment.md). For backups and disaster recovery, see [Backup & recovery](backup-and-recovery.md). --- ## 1. The read-only ops CLI — `bin/arcodange` [`bin/arcodange`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/bin/arcodange) is a Bash dispatcher that gives a human-friendly entry point to safe, **strictly read-only** Dolibarr operations against `erp.arcodange.lab`. Every subcommand `exec`s a script under `.claude/skills//scripts/`; the dispatcher itself only locates the project root (via `git rev-parse --show-toplevel`, falling back to walking up from the script) and routes arguments. > [!IMPORTANT] > The CLI authenticates with credentials read from [`.claude/skills/dolibarr/.env`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/bin/arcodange) — a **gitignored** file expected at **mode `600`**. The underlying API key belongs to the `ai_agent` service account, which has **no write permissions**: the CLI cannot mutate Dolibarr. Corrections always go through the Dolibarr web UI. ### Command map | Command | Subcommand | What it does | | --- | --- | --- | | `ping` | — | `GET /status` — liveness probe + reports the running Dolibarr version | | `whoami` | — | `GET /users/info` — confirms auth is the `ai_agent` service account | | `invoice` | `list [--since YYYY-MM-DD]` | Table of KissMetrics customer invoices with payment state | | `invoice` | `audit ` | JSON facts + PDF mandatory-mention audit for one invoice | | `payments` | `state [--since YYYY-MM-DD]` | Per-invoice TTC vs payments reconciliation | | `payments` | `timeline [--year\|--since\|--until]` | Payment timeline with cumulative balance | | `payments` | `by-month [--year\|--all-clients]` | Monthly cash-receipt aggregation | | `tva` | `summary [--year\|--since\|--until]` | CA3-ready monthly TVA summary (collectée − déductible) | | `tva` | `collect` / `collect-detail` | TVA collectée by month × rate (CA3 A1/A4/E2) + per-line audit | | `tva` | `deductible` / `deductible-detail` | TVA déductible by month × rate (CA3 19/20/17+24) + per-line audit | | `thirdparty` | `audit ` | Country-aware completeness audit for one thirdparty | | `thirdparty` | `audit-all [--clients-only\|--suppliers-only]` | Audit every visible thirdparty | | `templates` | `list [--max-id N]` / `inspect ` | Enumerate / health-check recurring invoice templates | | `bank` | `probe` / `balance` / `match` / `qonto-transactions` / `wise-transactions` / `curl` | Qonto + Wise bank data and Dolibarr reconciliation | | `email` | `list` / `inspect ` / `curl` | Supplier-invoice ingestion from the Zoho mailbox | | `snapshot` | `--out FILE` (or `--print-only`) | Bundle the full read-only state into one JSON dump (with `content_hash`) | | `curl` | `` | Raw read-only call through `dol-curl.sh` (e.g. `arcodange curl /invoices/12`) | | `help` | `[command]` | Full command tree, or per-command help | ### Health checks first | Check | Command | Expected outcome | | --- | --- | --- | | Is Dolibarr up? | `bin/arcodange ping` | HTTP `200` + Dolibarr version string | | Is auth wired? | `bin/arcodange whoami` | The `ai_agent` user record | | Full state dump | `bin/arcodange snapshot --out /tmp/erp.json` | One JSON file with a `content_hash` | > [!TIP] > `snapshot` is the fastest way to capture a point-in-time, read-only view of the ERP (invoices, payments, TVA, thirdparties, templates) for offline diffing or for attaching to an incident. It does not touch the database — it only reads. The CLI's per-domain credentials beyond Dolibarr (Qonto/Wise for `bank`, Zoho OAuth for `email`) also live in the same gitignored `.env`. The skills' `SKILL.md` files remain the source of business-logic documentation; the CLI is just the ergonomic front door. --- ## 2. Static identity assets — `static/` [`static/`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/static) holds the company's legal identity and branding, consumed by the Playwright bootstrap suite when it configures a fresh Dolibarr install. | Path | Purpose | | --- | --- | | [`static/config/company.json`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/static/config/company.json) | Legal identity used for Dolibarr company setup and display | | [`static/img/logo512.png`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/static/img/logo512.png) | Company logo (referenced from `company.json` as `$IMG/logo512.png`) | | [`static/img/loginBackground.jpeg`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/static/img/loginBackground.jpeg) | Login-page background image | `company.json` carries two blocks — `info` (postal/contact identity) and `ID` (legal identity): | Field | Value | | --- | --- | | Raison sociale | Arcodange | | Forme juridique | SAS (Société par actions simplifiée) | | Adresse | 73 Boulevard de l'Yerres, 91000 Évry-Courcouronnes, France (FR) | | Site / email | arcodange.fr · gabrielradureau@arcodange.fr | | SIREN / SIRET | (legal registration identifiers) | | NAF / APE | 62.02A | | N° TVA | (intra-community VAT number) | | Capital | (share capital) | | RCS | R.C.S. Évry | | Mois début d'exercice | Juillet | | Logo | `$IMG/logo512.png` | > [!NOTE] > The `$IMG` token in `company.json` resolves to `static/img/` via the test harness's `IMG_FOLDER` (see §3). The same image folder feeds the optional login-page background and logo upload during display setup. --- ## 3. Bootstrap test suite — `test/` [`test/`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/test) is a **Deno + Playwright** UI suite that drives a real browser through the Dolibarr first-install and admin configuration flows. It is not a unit-test runner — it is the scripted bootstrap that stands up a fresh Dolibarr instance and applies the company identity. | File | Role | | --- | --- | | [`test/main.ts`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/test/main.ts) | Entry point: launches Chromium (`fr-FR` locale), wires `globalCtx`, runs install + admin setup steps | | [`test/deno.json`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/test/deno.json) | Imports `npm:playwright` and `jsr:@std/dotenv/load`; `checkJs: true` | | [`test/.env.example`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/test/.env.example) | Template for `DOLIBARR_ADDRESS`, DB password, admin login, `ROOT_FOLDER` | | [`test/scripts/admin/`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/test/scripts/admin) | `initialSetup.ts`, `companySetup.ts`, `displaySetup.ts`, `moduleSetup.ts` | ### Run the suite 1. Install Deno and the Playwright browsers: - `curl -fsSL https://deno.land/install.sh | sh` - `deno run --allow-all npm:playwright install` 2. Populate `test/.env` from the live cluster secrets (DB password from the `vso-db-credentials` secret; admin password from `secretkv`). See [Secrets & VSO](../tools/secrets-and-vso.md) for how those secrets land in the namespace. 3. Run: `deno run --allow-all main.ts`. ### Lock the installer after install > [!CAUTION] > Dolibarr's `install/` wizard stays reachable until an `install.lock` exists. After a successful first install, **always** create the lock — an unlocked installer is a live takeover risk on a production-like instance. The post-install step touches the lock file inside the pod and chowns it to `www-data`: ```sh kubectl -n erp exec $(kubectl get pod -n erp -l app.kubernetes.io/name=erp -o name) -- \ /bin/bash -c '/usr/bin/touch /var/www/html/install.lock && /bin/chown www-data:www-data /var/www/html/install.lock' ``` `initialSetup.isUpgradeLocked()` checks for the same locked state before deciding whether to (re)run the installer, so the lock is both a safety gate and the suite's idempotency signal. --- ## 4. Day-2 operations ### Scaling — manual only | Setting | Value | Source | | --- | --- | --- | | `replicaCount` | `1` | [`chart/values.yaml`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/chart/values.yaml) | | `autoscaling.enabled` | `false` | [`chart/values.yaml`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/chart/values.yaml) | Dolibarr runs as a **single replica with no HorizontalPodAutoscaler**. The instance is backed by a `ReadWriteOnce` filesystem PVC, so scaling out is not a supported topology — scaling is a deliberate, manual `replicaCount` change in the chart values, applied through the normal [deployment](deployment.md) path. Treat the workload as a single-writer system. ### After activating a new Dolibarr module — fix table ownership Activating a Dolibarr module creates new SQL tables, and Dolibarr's migration runner creates them under whatever role the live VSO-rotated credentials happen to map to. If that role is not `erp_role`, a subsequent credential rotation by Vault can leave the new tables unreadable. > [!IMPORTANT] > After enabling any new module (e.g. via `moduleSetup.configureModule`), run the table-ownership reassignment so the new objects are owned by `erp_role`. The `REASSIGN OWNED BY … TO erp_role` logic lives in [`chart/scripts/update_ownership.sql`](https://gitea.arcodange.lab/arcodange-org/erp/src/branch/main/chart/scripts/update_ownership.sql) and is also mounted into the pod entrypoint as `update_table_ownership.sql`. Apply it inside the pod: ```sh kubectl exec -n erp $(kubectl get pod -n erp -l app.kubernetes.io/name=erp -o name) -c erp -- \ sh -c 'PGPASSWORD=${DOLI_DB_PASSWORD} psql -U ${DOLI_DB_USER} -h ${DOLI_DB_HOST} \ -p ${DOLI_DB_HOST_PORT} ${DOLI_DB_NAME} -f /var/www/scripts/before-starting.d/update_table_ownership.sql' ``` If the pod logged `Read-only file system` for the `update_table_ownership.sql` step at startup (the entrypoint cannot write its temp file), the reassignment never ran — run the command above by hand. If the live DB user has already lost rights, run the same SQL with the **admin Postgres credentials** instead. The role model is described in [Postgres IaC](../factory-provisioning/opentofu/postgres-iac.md). ### Watch PVC usage so backups do not fill the volume ERP data and (where co-located) backup artifacts share storage on a single PVC. If on-volume backup snapshots accumulate, they can exhaust the volume and take Dolibarr down. | Watch | Why | | --- | --- | | PVC used vs. capacity | A full volume crashes Dolibarr (no room for sessions/temp/migrations) | | Backup artifact growth | Old dumps left on the volume eat the same space data needs | > [!WARNING] > Monitor PVC usage and prune/offload old backup artifacts before they fill the **50Gi** volume. Backup retention, artifact layout, and the off-volume target are documented in [Backup & recovery](backup-and-recovery.md) — keep on-volume copies short-lived. See [Storage & recovery](../lab-ecosystem/storage-and-recovery.md) for the durable-copy model. --- ## See also - [ERP hub](README.md) — overview and entry point for the ERP guidebook. - [Deployment](deployment.md) — how the workload, chart, and credentials reach the cluster. - [Backup & recovery](backup-and-recovery.md) — backup artifacts, retention, restore drills. - [Secrets & VSO](../tools/secrets-and-vso.md) — how `vso-db-credentials` / `secretkv` land in the namespace.