Arc OS — Architecture

Vue d'ensemble

Arc OS remplace 11 321 lignes d'infrastructure Python/JS sur mesure par des outils natifs Claude Code. L'agent IA est le backend.

Architecture système (Phase 52.1)

graph TB
    User[👤 Navigateur utilisateur :18888]
    TG[📱 Telegram]
    Local[💻 IDE local]

    subgraph "Edge"
      Nginx[Nginx :18888<br/>basic-auth + deny-list]
      FE[React CRM + Phaser<br/>conteneur frontend]
    end

    subgraph "Master Bot Bun :19210 — 127.0.0.1 uniquement"
      ApiSrv[api-server.ts<br/>196 LOC]
      Routes[master-bot/routes/<br/>auth · internal · cli · websocket]
      Router[shared/routes/router.ts<br/>373 LOC dispatcher]

      subgraph "19 modules domaine — Phase 48"
        D1[auth · projects · workers]
        D2[skills · sage · chat]
        D3[wiki · files · onboarding]
        D4[billing · invites · analytics]
        D5[+ 7 autres]
      end
    end

    subgraph "Bots fédérés"
      Master[Master Bot<br/>orchestrateur]
      Child[Child Bots<br/>par projet]
      Claude[claude -p CLI]
    end

    subgraph "Couche intelligence"
      Eval[Binary Evals]
      Ctx[Context Router top-5]
      Learn[Learnings inject]
      Karp[Karpathy nightly loop]
    end

    NB[NotebookLM Bridge<br/>FastAPI :19213 localhost]
    Google[(Google NotebookLM)]

    DB[(SQLite WAL<br/>30+ tables<br/>migrations 001-035)]
    Vault[(AES-256-GCM Vault<br/>config/vault.json)]
    Bridge[Local Bridge CLI<br/>relais WebSocket]

    User -->|HTTPS| Nginx
    Nginx --> FE
    Nginx -->|/api/crm/*| ApiSrv
    Nginx -->|/api/sse/*| ApiSrv
    Nginx -->|/ws/terminal| ApiSrv
    ApiSrv --> Routes
    Routes --> Router
    Router --> D1 & D2 & D3 & D4 & D5

    TG --> Master
    TG --> Child
    Master -.spawns via tmux.-> Child
    Child --> Claude
    Claude --> Eval & Ctx & Learn

    Router -.queries.-> NB
    Child -.auto-sync.-> NB
    NB --> Google

    Router --> DB
    Router --> Vault
    Child --> Vault

    Local --> Bridge
    Bridge -.ws relay.-> ApiSrv
    Karp -.nightly.-> DB

Faits clés :

LEGACY (v1) :
User → Telegram Bot (Python 111 Ko) → HybridEngine → command_queue.json
       → bridge_processor.sh → Claude → command_responses.json
       → FastAPI EventBus → WebSocket → Phaser UI

V2 (NATIVE-FIRST) :
User → Canal Telegram officiel → Session Claude Code
       ↓                                ↓
       reply/edit_message          Agent Teams (TaskCreate/SendMessage)
                                        ↓
                                   Écriture état → fichiers JSON
                                        ↓
                                   Arc OS Bridge MCP → HTTP/SSE → Phaser UI

Réduction : 11 321 lignes → ~3 700 lignes (67 % de moins)

Composants

1. Session Claude Code (Le cerveau)

La session Claude Code remplace :

Toute l'orchestration se fait nativement via Agent Teams.

2. Arc OS Bridge MCP Server (Adaptateur d'état)

Serveur MCP TypeScript (Bun) qui fait le pont entre l'état Claude Code et le frontend.

Outils MCP (côté Claude Code) :

Outil Direction Description
get_office_state lecture Lit state/office-state.json
update_agent_state écriture Met à jour position, statut, bulle d'un agent
get_tasks lecture Lit les fichiers de tâches Agent Teams
get_knowledge lecture Lit l'index de connaissance

HTTP API (côté frontend) :

Endpoint Méthode Description
/api/state GET État complet du bureau
/api/tasks GET Liste des tâches
/api/knowledge GET Index de connaissance
/api/events GET Flux SSE

3. Frontend Phaser (Couche visuelle)

Phaser 3.80 + Vite. Se connecte au serveur MCP via HTTP/SSE.

Différence clé par rapport à v1 : pas de WebSocket — remplacé par SSE depuis le serveur MCP. Pas d'UI de saisie de commandes — Telegram est l'interface de contrôle.

4. Interface de commande Telegram (Couche UI tactique — Phase 21.0)

Telegram est le panneau de contrôle principal de tout Arc OS. Deux niveaux de bots :

Master Bot (@citadel_ceo_bot) :

Child Bots (@cv2_pt_bot, etc.) :

Intégration CRM (développement) :

CRM_BASE_URL = http://62.171.128.248:18888  (dev)
               https://crm.citadel.v2       (prod futur)

Tous les layouts de clavier sont définis dans shared/ui_templates.ts — source unique pour les changements UI.

Protocole de données callback : action:target (max 64 octets)

Flux de données

1. L'utilisateur envoie "/status" via Telegram
2. La session Claude Code reçoit via le canal Telegram
3. Rick lit state/office-state.json + TaskList()
4. Rick répond via Telegram (edit_message pour les mises à jour en direct)
5. Rick appelle update_agent_state() via MCP
6. Le serveur MCP écrit dans state/office-state.json
7. Le file watcher détecte le changement → pousse un événement SSE
8. Le frontend reçoit SSE → met à jour les sprites d'agents

Gestion de l'état

Tout l'état réside dans des fichiers JSON sous state/ :

Fichier Schéma Mis à jour par Lu par
office-state.json Positions agents, statut, bulles Claude (via MCP) Frontend (via HTTP)
knowledge.json Métadonnées de fichiers indexés Agent Squanchy Frontend, Beth
reports/*.json Sorties d'analyse Beth, Summer Frontend

Les tâches Agent Teams sont gérées nativement par Claude Code dans ~/.claude/tasks/citadel-v2/.

Agents (6)

Agent Rôle Modèle Activation
Rick CEO/Orchestrateur opus-4.6 Toujours actif (chef d'équipe)
Morty SRE/Monitoring haiku-4.5 Vérifications santé, cron
Summer Mémoire/Revue sonnet-4.5 Revue de code, sécurité
Jerry Maintenance haiku-4.5 Nettoyage, docs
Squanchy Archiviste haiku-4.5 Indexation de fichiers
Beth Analyste sonnet-4.5 Recherche, analyse

Chaîne d'approvisionnement

CEO → Rick (décompose) → Agent Team (exécute)
                        → Revue croisée (Summer valide)
                        → Rick (synthétise)
                        → CEO (résultat final)

Appliquée via :

Stack technologique

Couche v1 v2
Backend FastAPI + Python 3.12 Session Claude Code
Base de données SQLite (8 tables) Fichiers JSON + Agent Teams
Messagerie WebSocket + EventBus Agent Teams SendMessage
État SQLite + pont JSON Fichiers JSON (state/)
Frontend Phaser 3.86 + WebSocket Phaser 3.80 + SSE
Telegram Bot personnalisé (111 Ko) Canal officiel plugin
Bridge bash + jq (19,6 Ko) Supprimé
MCP Aucun Arc OS Bridge (TypeScript)
Déploiement Docker Compose Docker Compose

Hooks de cycle de vie

Les hooks Claude Code (~/.claude/settings.json) synchronisent automatiquement l'état des agents :

Événement SubagentStop → subagent-stop.sh → office-state.json (agent=idle)
                                              ↓
                                   StateManager (fs.watch) → SSE → Phaser UI

Événement Stop → session-end.sh → latest_wrapup.txt + tous agents=idle

Scripts : scripts/citadel-hooks/subagent-stop.sh, scripts/citadel-hooks/session-end.sh

Champs clés : subagentName (SubagentStop), stopReason (Stop), cwd (les deux).

NotebookLM Bridge (Phase 36.3)

Recherche sémantique réelle via Google NotebookLM, remplaçant les uploads manuels de fichiers :

Cloud PM Chat → Bun Master Bot (:19210)
                    │
         executeAskNotebooklm()
                    │
         HTTP → FastAPI Bridge (:19213, localhost uniquement)
                    │
              notebooklm-py async client
                    │
              Google NotebookLM (recherche sémantique gratuite)

         Fallback : recherche par mots-clés locale (code existant)

L'export basé sur fichiers legacy (/citadel-wrapup, /citadel-recall) reste fonctionnel en fallback.

Politique de routage des données et tâches

Système à trois niveaux. Chaque niveau a un objectif strict. Mélanger les niveaux = chaos.

┌─────────────────────────────────────────────────────────────────┐
│  NIVEAU 1 : Mémoire de travail (Chaud)                          │
│  office-state.json + state/tasks/                               │
│  CLI : /citadel-task, /citadel-status                           │
│                                                                 │
│  Tâches agents quotidiennes. Vit pendant la session. Disparaît  │
│  après. Tâche terminée → /citadel-wrapup → Niveau 3.           │
├─────────────────────────────────────────────────────────────────┤
│  NIVEAU 2 : Backlog stratégique (Tiède)                         │
│  GitHub Issues (repos Claude-CEO + citadel-v2)                  │
│                                                                 │
│  Epics uniquement : Phase 20, Phase 21, bugs globaux.           │
│  Pas de tâches quotidiennes. Pas de tickets "fix typo".         │
│  Un ticket = une initiative plurisemaines ou un défaut critique.│
├─────────────────────────────────────────────────────────────────┤
│  NIVEAU 3 : Mémoire long terme (Froid)                          │
│  docs/library-export/ → Google Drive → NotebookLM              │
│  CLI : /citadel-wrapup, /citadel-recall                         │
│                                                                 │
│  Encyclopédie. Résultats de session, décisions archi, RAG.     │
│  Archive en lecture seule. Pas de tâches actives.               │
└─────────────────────────────────────────────────────────────────┘

Règles de routage

Type de données Niveau Exemple
"Corriger le script de déploiement" 1 — Mémoire de travail /citadel-task "Fix deploy script"
"Phase 20 : SaaS multi-tenant" 2 — GitHub Issues gh issue create --title "Phase 20: ..."
"Résumé session : hooks implémentés" 3 — Library /citadel-wrapup → NotebookLM
"Décision archi : choix SSE plutôt que WS" 3 — Library Archivé dans wrapup après session
"Bug : SSE se coupe sur le VPS" 2 — GitHub Issues Seulement si inter-session / bloquant
"Rick travaille sur les hooks" 1 — Mémoire de travail Statut agent office-state.json

Cycle de vie

CEO donne une tâche → Niveau 1 (l'agent travaille dessus)
                         ↓ terminée
                     /citadel-wrapup → Niveau 3 (archivée)
                         ↓ si stratégique
                     gh issue create → Niveau 2 (suivi long terme)

Anti-patterns (NE PAS FAIRE)

Infrastructure (Phase 20.5)

Logging structuré (shared/logger.ts)

Format JSONL, sortie double (fichier + console), découpage quotidien par catégorie.

/var/log/citadel/
├── master/
│   ├── system-2026-04-02.log   ← cycle de vie, config, santé
│   ├── dialog-2026-04-02.log   ← (non utilisé par master)
│   └── error-2026-04-02.log    ← erreurs (aussi dans system)
├── citadel-v2/
│   ├── system-2026-04-02.log
│   ├── dialog-2026-04-02.log   ← messages user ↔ Claude
│   └── error-2026-04-02.log
└── <project-name>/             ← par projet embarqué

Rotation des logs : config/logrotate-citadel.conf/etc/logrotate.d/citadel (quotidien, rétention 7 jours).

Vault de secrets (shared/vault.ts)

Stockage chiffré AES-256-GCM pour les tokens de bots.

Source clé : env SECRET_ENCRYPTION_KEY → fichier config/vault-key → auto-généré
Stockage :   config/vault.json (écritures atomiques : tmp + mv)
Cache :      En mémoire après initVault() — pas d'I/O disque par getSecret()
Fallback :   getSecret("FOO") → cache vault → process.env.FOO
Nommage :    child:<project-name>:token

Le token propre du Master reste dans .env (le vault se charge dans le processus master).

Watchdog auto-réparant (master-bot/watchdog.ts)

Moniteur en arrière-plan pour les child bots. S'exécute dans le processus du master bot.

Toutes les 30s : vérification santé HTTP → /api/child/health (timeout 5s)
  Sain → reset des échecs
  Non sain → incrément des échecs
  3+ échecs + backoff écoulé → redémarrage auto (kill tmux → démarrage nouveau avec token vault)
  Backoff : 30s → 1 min → 5 min → 15 min → 60 min (cap)
  10 échecs consécutifs → désactivation permanente + notification CEO

État persisté dans config/watchdog-state.json (survit au redémarrage du master). Notifications CEO via Telegram : premier redémarrage, échec de redémarrage, désactivation permanente. Commande /watchdog : statut en temps réel de tous les enfants.

Protocole de suppression de projet (Phase 20.4 + 21.0)

/remove_project <name> → triple confirmation → nettoyage complet :

1. Kill de la session tmux : child-<name>
2. Kill massif des fantômes : ps aux | grep child-<name> → kill -9 tous les PIDs
3. Vérification du port : ss -tlnp | grep :<port> → forcer libération si occupé
4. Retrait de bot_registry.json + rechargement en mémoire
5. Suppression de /opt/repos/<name>/ (sécurité : chemin doit être sous /opt/repos/, min 3 segments)
6. Suppression de /var/log/citadel/<name>/

Noms protégés : citadel-v2, citadel, claude-ceo, claude-CEO — suppression bloquée.

Problème de processus fantôme (leçon apprise) : après un kill tmux, des processus bun orphelins peuvent garder les ports. Le kill massif + vérification de port empêche les "bots fantômes" qui répondent aux vérifications santé mais ignorent Telegram.

Gestion des skills (Phase 21.0)

Les child bots chargent les skills depuis deux sources au démarrage, dédupliquées via Set :

Source 1 : MANIFEST.md (JSON)
  → manifest.skills[]          (matchés pendant l'onboarding)
  → manifest.library_skills[]  (fichiers .md de bibliothèque matchés)

Source 2 : répertoire skills/
  → fichiers *.md → nom de fichier sans extension

Fusion : Set<string>(manifest.skills + manifest.library_skills + skills/*.md)

Exemple (projet PT) : 5 depuis manifest + 7 depuis skills/ = 12 skills uniques.

Les skills apparaissent comme :

Sécurité (Phase 42 — Audit Sentinel 2026-04-23, 4 passes, 13 patches)

Vue d'ensemble complète : SECURITY.md. Rapport d'audit : security/audit-2026-04-23.md.

Secrets et stockage :

Auth :

Multi-tenancy (Phase 42) :

Périmètre réseau :

Sécurité entrées/chemins :

Couverture de régression : scripts/vps-sync.sh exécute 7+ tests smoke post-déploiement après chaque déploiement (bind loopback, traversal de chemins, validation chat/save, canari proxy, SSE ?token=, validation fail-closed, blocage SSRF si CEO_TOKEN défini).

Modules du Master Bot (Phase 25 — Résolution DEBT-1)

Le master bot est organisé en modules ciblés :

master-bot/
├── bot.ts               ← Bootstrap : vault, registry, contexte, câblage (106 lignes)
├── context.ts           ← Type MasterContext + interfaces domaine
├── api-server.ts        ← Bun.serve() — HTTP, WebSocket, SSE, routes CRM
├── tg-api.ts            ← Wrapper Telegram Bot API (sendMessage, getUpdates, etc.)
├── child-state.ts       ← Lire état child bot, santé, heartbeat, tmux, bridge
├── telegram-commands.ts ← Tous les handlers /commande, handler requête callback, routeur messages
├── telegram.ts          ← Boucle de polling légère + réexports pour compat rétrograde
├── watchdog.ts          ← Moniteur child bot auto-réparant
└── onboarding.ts        ← Assistant interactif de création de projet

bot.ts importe uniquement depuis telegram.ts et api-server.ts. Tous les autres modules sont des détails d'implémentation internes.

Bridge CLI (Phase 25 — Local Gateway)

bridge/
├── bin/citadel-bridge.ts    ← Entrée CLI (commander.js) : connect, pull, push, status, disconnect
├── src/
│   ├── auth.ts              ← Validation JWT contre CRM
│   ├── config.ts            ← Persistance ~/.citadel/bridge.json
│   ├── inject.ts            ← Marqueurs CLAUDE.md <!-- CITADEL:START/END -->
│   ├── sync.ts              ← Pull skills-bundle + learnings, push learnings locaux
│   └── heartbeat.ts         ← Rapporteur d'activité de session
├── package.json
└── tsconfig.json

Endpoints CRM API (50+ au total)

CRM principal (Phase 22)

Endpoint Méthode Objectif
/api/master/health GET Santé Master bot (public)
/api/crm/projects GET Liste tous les projets + santé en direct
/api/crm/projects/:name GET Détail du projet
/api/crm/projects/:name/logs GET Tail des logs JSONL
/api/crm/projects/:name/files GET Listing répertoire sécurisé
/api/crm/projects/:name/files/upload POST Upload de fichiers
/api/crm/projects/:name/files/mkdir POST Créer un dossier
/api/crm/projects/:name/files/create POST Créer un fichier
/api/crm/projects/:name/files/delete DELETE Supprimer fichier/dossier
/api/crm/projects/:name/files/clone POST Cloner un repo git
/api/crm/projects/:name/files/read GET Lire le contenu d'un fichier
/api/crm/projects/:name/wiki/tree GET Lister les fichiers .md du wiki
/api/crm/projects/:name/wiki/file GET Lire le contenu d'un fichier wiki
/api/crm/projects/:name/skills GET Skills installés
/api/crm/projects/:name/metrics GET Séries temporelles qualité
/api/crm/projects/:name/restart POST Redémarrer le child bot
/api/crm/projects/:name/specs GET Lister les specs
/api/crm/projects/:name/specs/:id/approve POST Approuver une spec
/api/crm/projects/:name/specs/:id/reject POST Rejeter une spec
/api/crm/projects/:name/active-role GET/POST Rôle de l'agent
/api/crm/projects/:name/message POST Mettre en file un message pour un worker
/api/crm/projects/:name/workers GET/POST CRUD registry workers
/api/crm/projects/:name/workers/:id PUT/DELETE Mettre à jour/supprimer un worker
/api/crm/projects/:name/workers/generate-prompt POST Générer un system prompt via IA
/api/crm/projects/:name/skills-bundle GET Skills + evals pour bridge
/api/crm/projects/:name/learnings GET/POST Sync learnings
/api/sse/logs/:name GET Flux SSE de logs (?category=)

Tableau de bord & Wiki (Phase 32)

Endpoint Méthode Objectif
/api/crm/projects/:name/wiki/save PUT Sauvegarder/créer une page wiki
/api/crm/projects/:name/skills/save PUT Sauvegarder un fichier skill
/api/crm/projects/:name/skills/delete DELETE Supprimer un fichier skill

Multi-tenant (Phase 33)

Endpoint Méthode Objectif
/api/crm/account/settings GET/PUT Clés API au niveau compte
/api/crm/projects/create POST Création légère de projet
/api/crm/onboarding/setup POST Assistant d'onboarding complet

MCP & CLI (Phase 34)

Endpoint Méthode Objectif
/api/mcp/issues/:project POST/GET CRUD tickets
/api/mcp/issues/:project/:id PUT Mettre à jour un ticket
/api/mcp/wiki/:project PUT Sync wiki via MCP
/api/mcp/roadmap/:project GET/PUT Lecture/sync roadmap
/api/cli/init/:project/:mode GET Contexte cloud pour CLI
/api/mcp/skills/:project/:skill GET Récupérer un skill pour MCP
/api/mcp/report/:project POST Rapport de mission
/api/mcp/learnings/:project GET Obtenir les learnings pour MCP

Terminal en direct (Phase 35)

Endpoint Méthode Objectif
/api/crm/projects/:name/terminal/log POST Recevoir JSONL terminal depuis ARC CLI

Cloud PM & Knowledge (Phase 36)

Endpoint Méthode Objectif
/api/crm/projects/:name/chat POST Proxy chat SSE vers Anthropic API
/api/crm/projects/:name/skills/generate POST Neural Skill Generator (NotebookLM)
/api/crm/projects/:name/notebooks GET Notebooks NotebookLM liés

Auth & OAuth (Phase 37)

Endpoint Méthode Objectif
/api/auth/register POST Inscription email/mot de passe
/api/auth/login POST Connexion email/mot de passe
/api/auth/google GET Redirection OAuth Google
/api/auth/callback/google GET Callback OAuth Google
/api/auth/github GET Redirection OAuth GitHub
/api/auth/callback/github GET Callback OAuth GitHub
/api/auth/providers GET Providers OAuth disponibles

Notes épinglées (Phase 41.8)

Endpoint Méthode Objectif
/api/crm/projects/:name/pins GET Lister les notes épinglées (plus récentes en premier)
/api/crm/projects/:name/pins POST Épingler un message worker dans le Context Rail (body : worker_id, body, title optionnel, author)
/api/crm/projects/:name/pins/:id DELETE Supprimer une note épinglée

Backend : migration 009 (table pinned_notes), pinnedNoteQueries dans shared/db.ts. Frontend : ContextRail.jsx récupère au montage, écoute l'événement CustomEvent crm-pin-created, DELETE optimiste.

Couche de protection des données (Phase 45)

Chiffrement hybride au repos — fondation crypto côté client + chiffrement côté serveur.

Côté client (navigateur)

Côté serveur (Bun + SQLite)

Headers de sécurité

Sanitisation PII

Endpoints de clé de récupération

Endpoint Méthode Objectif
/api/crm/account/recovery POST Créer une clé de récupération (stocke la clé maître chiffrée)
/api/crm/account/recovery GET Lister les clés de récupération actives
/api/crm/account/recovery DELETE Révoquer des clés de récupération
/api/crm/account/recovery/restore GET Récupérer la clé maître chiffrée pour la restauration

Détails sécurité complets : docs/SECURITY.md, docs/architecture/PHASE_45_E2EE.md.