Datenbank — SQLite Schema

Allgemeine Informationen

ER-Diagramm (Phase 52.1)

erDiagram
    users ||--o{ projects : owns
    users ||--|| account_settings : has
    users ||--o{ recovery_keys : holds
    users ||--o{ subscriptions : pays
    users ||--o{ invites : creates
    users ||--o{ chat_messages : sends
    users ||--o{ trial_credits : claims
    users ||--o{ activity_log : generates

    projects ||--o{ project_phases : tracks
    projects ||--o{ pinned_notes : pins
    projects ||--o{ chat_messages : contains
    projects ||--o{ timeline_events : records
    projects ||--o{ github_links : links
    projects ||--o{ skill_benchmarks : runs
    projects ||--o{ marketplace_cache : caches

    github_links ||--o{ github_events : receives

    invites }o--|| users : "consumed by"
    subscriptions ||--o{ stripe_events : "logs idempotency"

    users {
        TEXT id PK
        TEXT email UK
        TEXT password_hash
        TEXT role "admin · user"
        TEXT google_id UK
        TEXT github_id UK
        INTEGER email_verified
        INTEGER two_factor_enabled
        TEXT last_login
    }

    projects {
        INTEGER id PK
        TEXT technical_name
        TEXT owner_id FK
        TEXT display_name
        TEXT description
        TEXT color
        TEXT icon
        TEXT notebook_id "NotebookLM"
        INTEGER is_archived
    }

    chat_messages {
        INTEGER id PK
        TEXT project_name
        TEXT worker_id
        TEXT role "user · assistant · system"
        TEXT content "AES-256-GCM if encrypted=1"
        INTEGER encrypted
        TEXT timestamp
    }

    subscriptions {
        TEXT user_id PK
        TEXT plan "free · min · max · beta"
        TEXT stripe_customer_id
        TEXT status
    }

    invites {
        TEXT code PK "arc-XXXX-XXXX"
        TEXT created_by FK
        TEXT used_by FK
        TEXT parent_invite_code
        TEXT status "active · used · revoked"
    }

    recovery_keys {
        INTEGER id PK
        TEXT user_id FK
        TEXT encrypted_key
        TEXT key_hint "first 4 chars"
    }

    trial_credits {
        TEXT email PK
        INTEGER credits_used
        TEXT first_used_at
    }

23 Tabellen über 24 Migrationen. Auth + Projekte + Workspace-Daten + Intelligence + Billing + Beta-Gating + Context-Export-Audit. Alle FK-Constraints werden durchgesetzt.

Tabellen

users — Benutzer

Spalte Typ Beschreibung
id TEXT PK UUID
email TEXT UNIQUE E-Mail (Pflichtfeld)
password_hash TEXT Passwort-Hash (bcrypt)
role TEXT 'admin' oder 'user'
name TEXT Name
avatar_url TEXT Avatar-URL
google_id TEXT UNIQUE Google OAuth ID
github_id TEXT UNIQUE GitHub OAuth ID
email_verified INTEGER 0/1
two_factor_enabled INTEGER 0/1
totp_secret TEXT TOTP-Geheimnis (2FA)
last_login TEXT ISO 8601
created_at TEXT ISO 8601

projects — Projekte

Spalte Typ Beschreibung
id INTEGER PK Auto
technical_name TEXT Technischer Name (eindeutig pro Eigentümer)
owner_id TEXT FK→users Eigentümer
display_name TEXT Anzeigename
description TEXT Beschreibung
color TEXT Farbe (hex)
icon TEXT Symbol
is_archived INTEGER 0/1
sort_order INTEGER Reihenfolge (Standard 999)
notebook_id TEXT NotebookLM ID
project_protocol TEXT Projektprotokoll
created_at, updated_at TEXT Timestamps

UNIQUE(technical_name, owner_id)

account_settings — Konto-Einstellungen

Spalte Typ Beschreibung
user_id TEXT PK FK→users
anthropic_key TEXT Anthropic API-Schlüssel
openai_key TEXT OpenAI API-Schlüssel
name TEXT Name
role TEXT Rolle
auto_harvest INTEGER Automatisches Skills-Harvesting 0/1

chat_messages — Chat-Verlauf

Spalte Typ Beschreibung
id INTEGER PK Auto
project_name TEXT Projekt
worker_id TEXT Worker
role TEXT 'user', 'assistant', 'system'
content TEXT Nachrichtentext (AES-256-GCM verschlüsselt wenn encrypted=1)
encrypted INTEGER 0=plaintext, 1=vault-encrypted (Phase 45.3)
attachments TEXT JSON-Array
timestamp TEXT ISO 8601
metadata TEXT JSON-Objekt

INDEX(project_name, worker_id, timestamp DESC)

recovery_keys — Wiederherstellungsschlüssel (Phase 45.4)

Spalte Typ Beschreibung
id INTEGER PK Auto
user_id TEXT FK→users Eigentümer
encrypted_key TEXT Verschlüsselter Master-Key
key_hint TEXT Hinweis (erste 4 Zeichen)
created_at TEXT ISO 8601
used_at TEXT Zeitpunkt der Verwendung
revoked INTEGER 0=aktiv, 1=widerrufen

INDEX(user_id)

github_links — GitHub-Repo-Verknüpfungen (Phase 49.3)

Spalte Typ Beschreibung
id INTEGER PK Auto
project_name TEXT Arc OS Projekt
owner TEXT GitHub-Repo-Eigentümer
repo TEXT GitHub-Repo-Name
webhook_secret TEXT UNIQUE 32-Byte-Hex für HMAC-SHA256-Validierung
created_at TEXT ISO 8601
created_by TEXT FK→users Wer den Link erstellt hat

UNIQUE(project_name, owner, repo) — mehrere Repos pro Projekt erlaubt. INDEX(project_name), INDEX(webhook_secret)

github_events — Log der GitHub-Ereignisse (Phase 49.3.1)

Spalte Typ Beschreibung
id INTEGER PK Auto
link_id INTEGER FK→github_links CASCADE beim Löschen des Links
project_name TEXT Arc OS Projekt (denormalisiert für Query-Geschwindigkeit)
event_type TEXT push, pull_request, workflow_run, issues
action TEXT Sub-Aktion (opened/closed/success/failure)
summary TEXT Vorformatierter Anzeigestring
url TEXT GitHub Deep Link
actor TEXT GitHub-Benutzername
created_at TEXT ISO 8601

INDEX(project_name, created_at DESC), INDEX(link_id)

skills_global — Globale Skills

Spalte Typ Beschreibung
id INTEGER PK Auto
name TEXT UNIQUE Name
description TEXT Beschreibung
category TEXT Kategorie (Standard 'general')
content TEXT Skill-Inhalt (Markdown)
triggers TEXT JSON-Array der Trigger
keywords TEXT JSON-Array der Schlüsselwörter
eval_rules TEXT JSON-Array der Regeln
tool_code_ts TEXT TypeScript-Implementierung
version INTEGER Version (auto-increment)
status TEXT 'active', 'draft', 'deprecated', 'archived'

skills_project_forks — Skill-Forks

Spalte Typ Beschreibung
id INTEGER PK Auto
project_name TEXT Projekt
skill_id INTEGER FK→skills_global Eltern-Skill (CASCADE)
content TEXT Angepasster Inhalt
triggers, keywords, eval_rules, tool_code_ts TEXT Überschreibungen
version INTEGER Fork-Version

UNIQUE(project_name, skill_id)

skill_evolution_logs — Änderungshistorie der Skills

Spalte Typ Beschreibung
id INTEGER PK
skill_id INTEGER FK CASCADE
project_name TEXT Projekt (nullable)
action TEXT 'created', 'modified', 'applied', 'reverted'
diff_summary TEXT Änderungsbeschreibung
author TEXT Autor (Standard 'system')
metadata TEXT JSON

skill_update_requests — Skill-PRs (Sage)

Spalte Typ Beschreibung
id INTEGER PK
skill_id INTEGER FK CASCADE
proposed_by TEXT 'sage' (Standard)
status TEXT 'pending', 'approved', 'rejected', 'applied'
current_content TEXT Aktuelle Version
proposed_content TEXT Vorgeschlagene Version
reason TEXT Änderungsgrund

skill_benchmarks — A/B-Tests für Skills

Spalte Typ Beschreibung
id INTEGER PK
request_id INTEGER FK→skill_update_requests CASCADE
test_scenario TEXT Testszenario
old_output, new_output TEXT Ergebnisse
score_old, score_new REAL Bewertungen
judgment_reason TEXT Begründung

pinned_notes — Angeheftete Notizen

Spalte Typ Beschreibung
id INTEGER PK
project_name TEXT Projekt
worker_id TEXT Worker
title TEXT Titel
body TEXT Text
source_message_id INTEGER Verweis auf chat_messages

project_phases — Roadmap-Phasen

Spalte Typ Beschreibung
id INTEGER PK
project_name TEXT Projekt
phase_id INTEGER Phasennummer
phase_title TEXT Bezeichnung
status TEXT 'PLANNED', 'ACTIVE', 'COMPLETED', 'CANCELLED'
progress REAL 0.0–1.0
deadline TEXT ISO 8601

UNIQUE(project_name, phase_id)

activity_log — Aktivitätsjournal

Spalte Typ Beschreibung
id INTEGER PK
project_name TEXT Projekt
actor TEXT Worker oder Benutzer
event_type TEXT Ereignistyp
title TEXT Beschreibung
metadata TEXT JSON
created_at TEXT ISO 8601

INDEX(created_at DESC), INDEX(project_name, created_at DESC), INDEX(event_type, created_at DESC), INDEX(actor, created_at DESC)

system_configs — System-Einstellungen

Spalte Typ Beschreibung
key TEXT PK Schlüssel
value TEXT Wert
description TEXT Beschreibung

marketplace_analysis_cache — Marketplace-Cache

Cache für die Kompatibilitätsanalyse von Marketplace-Skills.

ephemeral_tokens — Kurzlebige Token (Issue #27)

Persistenter Speicher für One-Time-Token für OAuth-State, Passwort-Reset und E-Mail-Verifizierung. Vor Migration 022 lebten diese Token in einer In-Memory-Map und gingen bei jedem Neustart des Masters verloren, was den Google/GitHub-Login zerstörte (invalid_state).

Spalte Typ Beschreibung
token TEXT PK 32-Byte-Hex (randomBytes(32))
type TEXT NOT NULL oauth_state | password_reset | email_verification
payload TEXT NOT NULL JSON: {provider} für oauth_state, {email} für den Rest
expires_at INTEGER NOT NULL ms-Epoch — TTL: 10 Min. (oauth) / 30 Min. (reset) / 24 Std. (verify)

INDEX(type), INDEX(expires_at). Einmalige Verwendung: consume() liest in einer Transaktion den Payload und löscht die Zeile.

project_issues — Issues pro Projekt (Issue #53, Phase 53.14)

SQLite SSOT für Issues anstelle von projektspezifischen issues/issues.json-Dateien. Die Datei verbleibt auf der Festplatte als Derived Export (Mirror), wird per gitignore ausgeschlossen — die DB ist autoritativ.

Spalte Typ Beschreibung
project_name TEXT NOT NULL Name aus Registry; zusammengesetzter PK mit id
id INTEGER NOT NULL 1-basierte Pro-Projekt-Sequenz (max+1 beim Insert)
title TEXT NOT NULL Issue-Titel
body TEXT NOT NULL DEFAULT '' Beschreibung, Markdown
priority TEXT NOT NULL DEFAULT 'P2' P0 | P1 | P2 | P3
labels TEXT NOT NULL DEFAULT '[]' JSON-Array of Strings
status TEXT NOT NULL DEFAULT 'open' open | closed
created_at TEXT NOT NULL ISO-Timestamp
updated_at TEXT NOT NULL ISO-Timestamp
closed_at TEXT NULL ISO-Timestamp bei status=closed
activity TEXT NOT NULL DEFAULT '[]' JSON-Array {ts, type, author, text} — append-only Audit

PK (project_name, id) — jedes Projekt hat seinen eigenen unabhängigen ID-Raum. INDEX (project_name, status) — häufigster Filter (open only). Operationen befinden sich in issueQueries (shared/db.ts): list/get/nextId/insert/upsert/replaceAll/bulkImport. replaceAll — atomare Transaktion; bulkImportINSERT OR IGNORE für idempotentes Re-Seeding.

onboarding_progress — Post-Wizard-Checklisten-Status (Issue #56, Phase 54.1)

Benutzerspezifischer Derived Cache über den activity_log-Eventstream (SSOT). Die UI liest mit einer einzelnen Abfrage, anstatt über Ereignisse zu aggregieren. Replaybar: Dismiss/Replay ändert nicht den state, nur dismissed_at.

Spalte Typ Beschreibung
chat_id TEXT PRIMARY KEY Benutzer-ID (Registry Chat-ID)
state TEXT NOT NULL DEFAULT '{}' JSON {workers, cli, skill, bot, issue}completed | skipped (fehlende Schlüssel = pending)
completed_count INTEGER NOT NULL DEFAULT 0 Derived-Cache-Zähler der completed-Schritte (0–5)
started_at TEXT NOT NULL DEFAULT (datetime('now')) Zeilenerstellung (erstes Ereignis/Dismiss/Replay)
completed_at TEXT NULL ISO-Timestamp wenn alle 5 Schritte completed
dismissed_at TEXT NULL ISO-Timestamp wenn der Benutzer die Checkliste geschlossen hat
source TEXT NOT NULL DEFAULT 'web' web | cli — für Funnel-Attribution (#58 arc tour)
updated_at TEXT NOT NULL DEFAULT (datetime('now')) Mutations-Timestamp

INDEX auf completed_at für Funnel-Analytik (#61 Phase 54.6). Operationen befinden sich in onboardingQueries (shared/db.ts): getProgress/recordEvent/dismiss/replay. recordEvent ist idempotent auf (chat_id, step, status) — ein wiederholter identischer Aufruf gibt changed=false zurück und schreibt nicht in activity_log. Whitelist: 5 Schritte × 2 Statuses (completed, skipped). skipped inkrementiert completed_count NICHT. Der Übergang skipped → completed erhöht den Zähler. Alle Mutationen emittieren Ereignisse in activity_log mit event_type LIKE 'onboarding_%' — echter SSOT für Funnel-Metriken; Tabellenspalten sind Derived Cache.

platform_audit_log — Super-Admin-Protokoll für Secret-Rotation (Phase 57, Sentinel #103)

Append-only-Journal für Mutationen an Vault-Platform-Secrets über die Platform-Einstellungen-UI. SSOT für Post-Incident-Forensik ("welcher Admin hat den Anthropic-Key um 03:14 UTC rotiert?"). vault.json selbst schreibt dies nicht — nur den Wert.

Spalte Typ Beschreibung
id INTEGER PRIMARY KEY AUTOINCREMENT Zeitliche Reihenfolge auch bei Clock-Skew
ts TEXT NOT NULL DEFAULT (datetime('now')) Server-seitige UTC; Angreifer können kein Backdating vornehmen
user_chat_id TEXT NOT NULL Admin, der die Aktion ausgeführt hat
user_email TEXT NULL Snapshot aus der users-Tabelle zum Zeitpunkt der Aktion
action TEXT NOT NULL list | view | rotate | test | restart
key_name TEXT NOT NULL Vault-Eintrag-Name (Allowlist in platform.ts); * für list-all
ip TEXT NULL aus CF-Connecting-IP / X-Real-IP / XFF-Tail
result TEXT NOT NULL success oder fail:<reason>
user_agent TEXT NULL UA-String, gekürzt auf 200 Zeichen

INDEXES: idx_platform_audit_ts (aktuell über alle Keys), idx_platform_audit_user (Pro-Admin-Trail), idx_platform_audit_key (Rotations-Historie pro Key). Append-only-Invariante: keine UPDATE/DELETE-Handler; UI stellt nur recent() + lastRotated() über platformAuditQueries in shared/db.ts bereit. Jede Aktion (Erfolg + Fehler) schreibt eine Zeile.

Migrationen

# Name Phase Was hinzugefügt wird
001 initial_schema Phase 40 users, projects, account_settings
002 project_protocol Phase 40.7 projects.project_protocol
003 chat_messages Phase 40.10 chat_messages
004 skill_system Phase 41 skills_global, skills_project_forks, skill_evolution_logs, skill_update_requests
005 skill_benchmarks Phase 40.11d skill_benchmarks
006 system_configs system_configs + FIGMA_CACHE_ROOT
007 marketplace_cache Phase 40.12.1 marketplace_analysis_cache
008 harvester Phase 40.13 +is_suggestion, +source_query, +auto_harvest
009 pinned_notes Phase 41.8 pinned_notes
010 project_phases Phase 44 project_phases
011 activity_log Phase 44.1 activity_log
012 performance_indexes Perf-Audit +idx_activity_log_actor, +idx_chat_msg_role_ts
013 timeline_timestamps Phase 47 +timestamp auf Worker-Nachrichten
014 timeline_events Phase 47 Tabelle timeline_events
015 e2ee_chat Phase 45.3 +encrypted-Spalte auf chat_messages
016 recovery_keys Phase 45.4 Tabelle recovery_keys
017 github_links Phase 49.3 Tabelle github_links (Webhook-Bindungen)
018 github_events Phase 49.3.1 Tabelle github_events (Ereignis-Log für UI-Feed)
019 trial_credits Phase 50.1 +users.trial_granted_at, +projects.trial_mode, +projects.trial_tokens_remaining
020 subscriptions Phase 51 Tabelle subscriptions (plan, stripe_customer_id, status, feature_flags) + stripe_events-Idempotenz-Log
021 invites Phase 52.1 Tabelle invites (Closed-Beta-Enrollment) — code, created_by, used_by, parent_invite_code, status, note
022 ephemeral_tokens Issue #27 Tabelle ephemeral_tokens — persistenter OAuth-State / Passwort-Reset / E-Mail-Verifizierung, anstelle von In-Memory-Map (Behebung von invalid_state beim Master-Neustart)
023 project_issues Phase 53.14 / Issue #53 Tabelle project_issues (zusammengesetzter PK project_name+id, JSON labels/activity) — verschiebt Issues-Storage von issues/issues.json in SQLite SSOT, JSON wird Derived Mirror; eliminiert Text-Merge-Drift beim Deploy
024 context_exports Phase 56.4 + 56.5 / Issues #100 + #101 export_audit_log (id PK, project_name, owner_id, exported_at, sections JSON, findings_critical/high/medium/low INT, bytes) + export_preferences (project_name PK, always_include_emails BOOL, auto_redact_critical BOOL DEFAULT 1, notify_on_export BOOL DEFAULT 0, updated_at) — projektweite Audit-Historie für Project Context Export + projektspezifische Policy-Toggles; Alert bei ≥3 Exporten/24h wenn notify_on_export = true
025 onboarding_progress Phase 54.1 / Issue #56 onboarding_progress (chat_id PK, JSON-State pro 5 Schritte, completed_count derived, started_at/completed_at/dismissed_at, source web|cli) — Derived Cache über activity_log-Eventstream; SSOT = Ereignisse event_type LIKE 'onboarding_%'; recordEvent idempotent, dismiss/replay non-destruktiv
026 platform_audit_log Phase 57 / Sentinel #103 platform_audit_log (id PK AUTOINCREMENT, ts, user_chat_id, user_email, action, key_name, ip, result, user_agent) — Append-only-Audit-Trail für Super-Admin-Platform-Einstellungen-UI-Mutationen an Vault-Secrets. 3 Indexes: idx_platform_audit_ts/user/key. Kein UPDATE/DELETE — schreibgeschützt über platformAuditQueries.recent + lastRotated
027 auth_events Issue #130 auth_events (id PK AUTOINCREMENT, ts, event, provider, user_id, email, ip, user_agent, result, meta JSON) — Append-only IP- + Auth-Ereignisprotokoll. Ereignisse: signup/login/oauth_login/magic_link/device_code_approve. Ergebnisse: success/failed/rate_limited/invalid_invite/already_registered/2fa_required/invalid_token. 4 Indexes: ts/user_id/ip/event+result
028 managed_containers Phase 60 #135/#136 managed_containers (id TEXT PK (Docker-Container-Name), user_id, status (provisioning|ready|paused|suspended|deleted), server_ip, internal_port INT, claude_authed INT, github_authed INT, created_at, last_active) — 1-pro-User Docker-Container-Registry für Standard Cloud. managedContainerQueries.findByUser/insert/updateStatus/setAuthFlag/touch/listActive in shared/db.ts. Endpunkte: POST /api/crm/cloud/provision, GET /status, POST /deprovision in shared/routes/cloud.ts
029 cloud_waitlist Phase 60 #134 cloud_waitlist (id INT PK AUTOINCREMENT, user_id TEXT UNIQUE, email, position INT, status (waiting|invited|activated|declined), notes, joined_at, invited_at, activated_at) — Early-Access-Warteliste für Standard Cloud. cloudWaitlistQueries.join/findByUser/list/invite/activate/decline/stats in shared/db.ts. Endpunkte: POST /api/crm/cloud/waitlist (beitreten), GET /waitlist/status (eigener Status), GET /waitlist (Admin-Liste), POST /waitlist/invite (Admin aktiviert + upgraded Plan auf cloud)
030 translation_feedback Phase 59.4 #122 translation_feedback (id INT PK AUTOINCREMENT, locale TEXT, message_id TEXT, original TEXT, current_translation TEXT, suggestion TEXT, user_id TEXT, status (pending|accepted|rejected), created_at, reviewed_at) — Per-Locale Übersetzungsqualitäts-Feedback. Admin-Review-Dashboard unter /api/crm/translations/feedback (list/stats/accept/reject).
031 token_usage_log Phase 63 #148 token_usage_log (id INT PK AUTOINCREMENT, project_name TEXT NOT NULL, owner_id TEXT NOT NULL, worker_id TEXT, input_tokens INT DEFAULT 0, output_tokens INT DEFAULT 0, cache_tokens INT DEFAULT 0, total_tokens INT DEFAULT 0, created_at INT DEFAULT (unixepoch())) — Per-Request Claude Token-Usage-Log. 3 Indexes: owner_id, project_name, created_at. Befüllt von child-bot/claude-runner.ts via POST /api/internal/usage/log (fire-and-forget). Abgefragt von GET /api/crm/account/usage → gibt { rows: [...letzte 200], totals: { total, input, output } } zurück für BillingPage + UserDropdown UsageCard.
032 arc_help_usage Phase 61 #147 arc_help_usage (id INT PK AUTOINCREMENT, user_id TEXT NOT NULL, date TEXT NOT NULL (YYYY-MM-DD), count INT NOT NULL DEFAULT 0, PRIMARY KEY (user_id, date)) — Täglicher Rate-Limit-Zähler für In-App-KI-Hilfe-Chat (30 Nachrichten/Tag). Abgefragt von GET /api/crm/help/usage; aktualisiert bei POST /api/crm/help/chat.

Beziehungen (Foreign Keys)

Alle CASCADE — das Löschen eines Skills löscht alle Forks, die Historie, PRs und Benchmarks.

ER-Diagramm (Textform)

users ─1:N──> projects
            ├── chat_messages
            ├── pinned_notes
            ├── project_phases
            └── skills_project_forks ──> skills_global
                                          ├── skill_evolution_logs
                                          ├── skill_update_requests
                                          │    └── skill_benchmarks
                                          └── (standalone tables)
                                               ├── activity_log
                                               ├── system_configs
                                               └── marketplace_analysis_cache