# Arcodange Lab — Agent Guide & Operating Rules The Arcodange lab is a self-hosted home/company platform running on three Raspberry Pi (pi1/pi2/pi3) behind a home Livebox, driven from a MacBook Pro M4 control node. **factory** is the cornerstone admin repo: it provisions the cluster (k3s), defines what gets deployed (ArgoCD app-of-apps), manages cloud/forge state (OpenTofu), and provisions databases (PostgreSQL). Two sibling repos carry workloads: **tools** (platform services — Vault, Prometheus, Grafana, CrowdSec, poolers) and **cms** (the public Nuxt site `arcodange.fr`). Everything is deployed into k3s namespaces via ArgoCD, every secret comes from Vault, and public traffic enters through a Cloudflared Zero-Trust tunnel into the internal Traefik. ```mermaid %%{init: {'theme':'base'}}%% flowchart TB subgraph control["Control node (MacBook Pro M4)"] ansible["Ansible"]:::proc tofu["OpenTofu"]:::proc end factory["factory repo
orchestrator: ansible + argocd + iac + postgres + doc"]:::src tools["tools repo
Vault, Prometheus, Grafana, CrowdSec, poolers"]:::src cms["cms repo
Nuxt site arcodange.fr"]:::src subgraph cluster["k3s cluster (pi1 server, pi2/pi3 agents)"] argocd["ArgoCD app-of-apps"]:::proc vault["Vault + VSO"]:::store nsapps["namespaces: tools / cms / webapp / erp / ..."]:::proc traefik["internal Traefik"]:::proc end cflared["Cloudflared Zero-Trust tunnel"]:::proc public["public: *.arcodange.fr"]:::store ansible -- "provision k3s + base" --> cluster tofu -- "state in GCS" --> factory factory -- "defines Applications" --> argocd argocd -- "deploy charts" --> tools argocd -- "deploy charts" --> cms tools --> nsapps cms --> nsapps vault -- "inject secrets" --> nsapps nsapps --> traefik cflared -- "ingress" --> traefik public --> cflared classDef src fill:#2563eb,stroke:#1e40af,color:#fff classDef proc fill:#059669,stroke:#047857,color:#fff classDef store fill:#7c3aed,stroke:#6d28d9,color:#fff ``` 1. The **control node** (MacBook Pro M4) runs Ansible and OpenTofu — the two hands that build everything. 2. **Ansible** provisions the **k3s cluster** (pi1 server, pi2/pi3 agents) and its base layer. 3. **OpenTofu** manages forge/cloud state, persisted in **GCS** (`gs://arcodange-tf`), serving the **factory** repo's intent. 4. **factory** defines the **ArgoCD app-of-apps**, which is the single deployment authority. 5. **ArgoCD** deploys the Helm charts of **tools** and **cms** into per-app k3s **namespaces**. 6. **Vault** (with the Vault Secrets Operator) injects secrets into those namespace pods — no secrets live in git. 7. Workload pods route through the **internal Traefik**. 8. The **Cloudflared Zero-Trust tunnel** is the only public ingress, forwarding `*.arcodange.fr` traffic into internal Traefik. --- ## Repos at a glance | Repo | Purpose | Key dirs | How deployed | |---|---|---|---| | **factory** | Cornerstone admin repo: provisions the cluster, defines deployments, owns infra state and DBs, holds canonical docs | [ansible/](ansible/) · [argocd/](argocd/) · [iac/](iac/) · [postgres/](postgres/) · [doc/](doc/) | Ansible (cluster + base) + OpenTofu (forge/cloud/PG state); ArgoCD reads its app-of-apps | | **tools** | Platform services in the `tools` namespace | [hashicorp-vault](https://gitea.arcodange.lab/arcodange-org/tools/src/branch/main/hashicorp-vault), [prometheus](https://gitea.arcodange.lab/arcodange-org/tools/src/branch/main/prometheus), [grafana](https://gitea.arcodange.lab/arcodange-org/tools/src/branch/main/grafana), [crowdsec](https://gitea.arcodange.lab/arcodange-org/tools/src/branch/main/crowdsec), [pgbouncer](https://gitea.arcodange.lab/arcodange-org/tools/src/branch/main/pgbouncer) | Helm/Kustomize charts deployed via ArgoCD | | **cms** | Public Nuxt static site `arcodange.fr` + its zone/email IaC | [chart](https://gitea.arcodange.lab/arcodange-org/cms/src/branch/main/chart), [cloudflare](https://gitea.arcodange.lab/arcodange-org/cms/src/branch/main/cloudflare), [zoho](https://gitea.arcodange.lab/arcodange-org/cms/src/branch/main/zoho) | Helm chart via ArgoCD; Cloudflare/Zoho via OpenTofu | Self-hosted Gitea is at `gitea.arcodange.lab` (org `arcodange-org`). **Pick the forge tool from the remote**: these repos live on Gitea, so use the `mcp__gitea__*` MCP tools for PRs/issues/releases — `gh` will silently fail. ## The `` join key One kebab-case identifier — `` — is reused **identically** across the Gitea repo, the PG database + `_role`, Vault (`postgres/creds/`, k8s auth role ``, policies `` / `-ops`, `gitea_cicd_`), the k8s namespace + ServiceAccount, the ArgoCD Application, the GCS state prefix `/main`, and DNS (`.arcodange.lab` / `.arcodange.fr`). Bricks wire together **by name convention, not explicit config**, so a single typo breaks the chain silently. Source of truth: [doc/runbooks/new-web-app/conventions.md](doc/runbooks/new-web-app/conventions.md); concept page (going forward): [vibe/guidebooks/lab-ecosystem/naming-conventions.md](vibe/guidebooks/lab-ecosystem/naming-conventions.md). ## Where knowledge lives Start at the knowledge-base front door: [vibe/README.md](vibe/README.md). The six `vibe/` folders: - [vibe/ADR/](vibe/ADR/README.md) — architecture decision records (the *why*); canonical home going forward. - [vibe/PRD/](vibe/PRD/README.md) — product/project requirement docs, each with a mandatory `STATUS.md`. - [vibe/investigations/](vibe/investigations/README.md) — numbered investigations (`INV-NNN-slug`), with notebooks when data-heavy. - [vibe/guidebooks/](vibe/guidebooks/README.md) — tree-docs that map the lab's components (the *how it fits together*). - [vibe/runbooks/](vibe/runbooks/README.md) — step-by-step operational procedures with `[AGENT]` / `[HUMAN]` markers. - [vibe/shareouts/](vibe/shareouts/README.md) — handouts and presentations (FRENCH; the one exception to the English rule). Historical infra docs still live under [doc/](doc/) (ADRs, the new-web-app runbook) — see also `CLUSTER_RECOVERY.md` (at the lab root, **outside** this repo) for tested power-cut recovery. ## Operating rules for agents ### No-tombstone rule (FOREMOST) Write every file as **currently true**. NEVER leave "Correction (date): …", "previously X, now Y", changelogs, or "updated to …" notes — git history is the audit trail. The **only** allowed exception is a forward-looking `> [!CAUTION]` about a live operational risk. ### Mermaid preferences Begin each block with an init directive selecting `theme base` (or `forest`). Define a `classDef` palette legible on both light and dark backgrounds (dark fills + light text), e.g. `classDef src fill:#2563eb,stroke:#1e40af,color:#fff`. Use HTML `
` for line breaks (never `\n`). Put a leading space before any label starting with a slash, and escape angle brackets inside labels. **Validate every diagram with the Mermaid MCP** before committing, and **immediately after each diagram add a numbered ordered list restating the same flow in words**. ### Tree-docs (guidebooks & big PRDs) Guidebooks and large PRDs are written as navigable trees: every file's **first line is a breadcrumb** (ancestors are relative links, current page is bold-unlinked, separator ` > `); **every folder has a `README.md` index hub** (a table of its children — link + one-line summary + status — sorted by importance/sequence, not alphabetically); **cross-references are bidirectional** (if A links B, B links A); use numbered file prefixes only for ordered narratives; **stamp `Last Updated: 2026-06-23` at each tree root**. ### Optimized ADR format (MADR-lite) Sections: **Context / Decision / Consequences / Alternatives / QA & validation / References**. Once an ADR is **Accepted, the body is immutable** — only the status field mutates (Proposed → Accepted → Superseded). The canonical home going forward is [vibe/ADR/](vibe/ADR/README.md); [doc/adr/](doc/adr/README.md) stays as the historical record. ### Investigations Prefer a **single `INV-NNN-slug.md`** when the finding fits in one file. When data-heavy, write a short stub `.md` beside a same-named folder containing notebooks (`.ipynb` + paired `.py`), a `_data/` dir, and a plain-language `notebook_simple.md` (visuals anyone can read). ### PRD convention **One subfolder per PRD.** A `STATUS.md` is **mandatory** and must be updated whenever something ships. Big PRDs use tree-docs and **must detail a QA strategy**. The flow is Problem → … → QA strategy → STATUS.md. ### PR crosslinking (bidirectional) **Every PR body references the ADR/PRD it advances.** In return, the ADR's **References** section and the PRD's **STATUS.md** link back to that PR. Links must be bidirectional — never one-way. ### Guidebook maintenance Altering a component that is documented in `guidebooks/` **requires updating that guidebook page in the same change**. A code/infra change that leaves its guidebook stale is incomplete. ### Language policy **English** for everything in `vibe/` and for `AGENTS.md`/`CLAUDE.md` (this tree is for LLM agents). The single exception: **shareouts handouts are FRENCH**. ## The cohort + workflow These personas are spawned via the Agent tool or a Workflow; document and reuse them across sessions: | Persona | Role | |---|---| | **Lab Cartographer** | Explores the three repos and maps them into guidebooks (tree-docs). Read-mostly; never edits infra. | | **ADR Scribe** | Writes optimized MADR-lite ADRs; enforces immutability (status-only mutation) and PR crosslinks. | | **PRD Architect** | Writes PRDs (Problem → … → QA strategy → STATUS.md); uses tree-docs for big ones. | | **Runbook Engineer** | Writes step-by-step runbooks with `[AGENT]` (read-only, safe) and `[HUMAN]` (prod-mutating, needs approval) markers. | | **Investigator** | Writes investigations: a single `INV-NNN-slug.md` when possible, else a stub `.md` beside a same-named folder with `.ipynb` + paired `.py`, a `_data/` dir, and `notebook_simple.md`. | | **Diagram Smith** | Authors and validates every mermaid diagram with the Mermaid MCP validator; enforces the mermaid preferences + the ordered-list-after-diagram rule. | | **Continuity Warden** | The adversarial reviewer: checks the no-tombstone rule, breadcrumb depth, bidirectional links, dead links, naming, STATUS/PR crosslinks, and the Last Updated stamp. | **Recommended workflow for substantial `vibe/` contributions:** 1. **Scaffold** — folders + README hubs + templates. 2. **Author** — personas in parallel, each on distinct files. 3. **Validate** — Diagram Smith runs the Mermaid MCP on every diagram. 4. **Review** — Continuity Warden runs the adversarial checklist. 5. **Assemble** — wire cross-refs, run the dead-link self-test, stamp `Last Updated`.