Datenbank — SQLite Schema

Allgemeine Informationen

ER-Diagramm (Phase 65)

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
        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)
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' draft | open | in_progress | blocked | deferred | closed (SQL-Default 'open' bleibt erhalten — neue Issues werden auf App-Ebene in draft gezwungen)
assignee TEXT NULL Worker-ID, die das Issue übernommen hat (Migration 036)
created_by TEXT NOT NULL DEFAULT 'legacy' Worker-ID, die das Issue erstellt hat (Migration 046 / #327) — für Audit (wer, wann, an wen)
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.

Migration 036 (#187, 2026-05-23): ALTER TABLE project_issues ADD COLUMN assignee TEXT. Neue Statuses (in_progress, blocked, deferred) werden auf App-Ebene validiert (IssueStatus-Union in shared/db.ts), nicht per CHECK-Constraint (SQLite erlaubt kein Hinzufügen von Constraints via ALTER TABLE).

Migration 046 (#327, 2026-06-03): ALTER TABLE project_issues ADD COLUMN created_by TEXT NOT NULL DEFAULT 'legacy'. Lifecycle um den neuen Status draft erweitert. handleCreateIssue erzwingt status='draft' wenn assignee=null (sonst direkt 'open'), verlangt worker_id (Body oder Auth-Fallback). handleUpdateIssue macht Auto-Promote draft → open bei der ersten Assignee-Zuweisung und lehnt close für never-assigned Issues ab (FR-ISS-101/102/103). Bereits vorhandene Zeilen werden per DEFAULT als 'legacy' backfilled.

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.

auth_events — Authentifizierungs-Ereignislog für Endnutzer (Migration 027, Issue #130)

Append-only-Journal der Auth-Ereignisse für Security-Auditing und Incident-Untersuchungen. Getrennt von platform_audit_log (Admin-only Vault) — diese Tabelle deckt den End-User-Auth-Flow inkl. IP ab.

Spalte Typ Beschreibung
id INTEGER PRIMARY KEY AUTOINCREMENT zeitliche Reihenfolge
ts TEXT NOT NULL DEFAULT (datetime('now')) server-seitige UTC
event TEXT NOT NULL signup | login | oauth_login | magic_link | device_code_approve
provider TEXT NOT NULL DEFAULT 'password' password | google | github | magic_link | device_code
user_id TEXT NULL NULL bei fehlgeschlagenen Versuchen, wenn der User nicht identifiziert wurde
email TEXT NULL beim Versuch verwendete E-Mail
ip TEXT NOT NULL DEFAULT 'unknown' CF-Connecting-IP → X-Real-IP → letztes XFF-Segment
user_agent TEXT NULL UA-String
result TEXT NOT NULL success | failed | rate_limited | invalid_invite | already_registered | 2fa_required | invalid_token
meta TEXT NULL JSON-Zusatzkontext (z. B. {isNew: true} bei OAuth)

INDEXES: idx_auth_events_ts (neueste zuerst), idx_auth_events_user (Historie pro User), idx_auth_events_ip (Bedrohungserkennung pro IP), idx_auth_events_event (Filter event+result). authEventQueries.insert/recent in shared/db.ts. Geloggt in master-bot/routes/auth.ts (register/login/oauth/magic-link) und master-bot/routes/cli.ts (device_code_approve).

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.
033 arc_help_messages Phase 61 #153 arc_help_messages (id PK AUTOINCREMENT, user_id, role, text, sources JSON, created_at) — Persistente Arc-Help-Chat-Historie pro User (letzte 60).
034 skills_owner_project #157 ALTER TABLE skills_global ADD COLUMN owner_project TEXT — NULL=global, non-null=gehört dem Projekt.
035 password_version Phase 64 #174 ALTER TABLE users ADD COLUMN password_version INTEGER DEFAULT 0 — wird bei Passwortänderung inkrementiert; der JWT-Claim pv wird von crmAuthMiddleware validiert, um alte Tokens zu invalidieren.
036 issue_assignee #187 / 2026-05-23 ALTER TABLE project_issues ADD COLUMN assignee TEXT — nullable Worker-ID. Ermöglicht arc issue take <id> + erweiterte Statuses in_progress/blocked/deferred (auf App-Ebene validiert).
046 issue_draft_and_created_by #327 / 2026-06-03 ALTER TABLE project_issues ADD COLUMN created_by TEXT NOT NULL DEFAULT 'legacy' — Audit-Feld „wer hat erstellt". Lifecycle += draft (App-Level-Union). handleCreateIssue erzwingt draft ohne Assignee; handleUpdateIssue Auto-Promote draft→open beim Zuweisen + lehnt close für never-assigned ab (FR-ISS-101/102/103).
037 waitlist #197 / 2026-05-25 waitlist (id INT PK AUTOINCREMENT, email TEXT UNIQUE, message, status DEFAULT 'pending', created_at, updated_at) — Early-Access-Anfrageformular auf der Login-Seite.
038 plata_billing Phase 65 #202 / 2026-05-26 Fügt Plata-by-mono-Spalten zu subscriptions hinzu: plata_card_token TEXT, plata_wallet_id TEXT, plata_masked_pan TEXT, next_billing_date TEXT, billing_failures INTEGER DEFAULT 0. Erstellt außerdem die Idempotenz-Tabelle plata_events (invoice_id PK, status, processed_at).
039 skill_project_grants #201 / 2026-05-26 skill_project_grants (skill_id INT FK→skills_global.id CASCADE, project_name TEXT, granted_at, PK composite) — Many-to-many-Skill-Zugriff über Projekte hinweg.
040 drop_stripe #205 / 2026-05-26 DROP der Spalte stripe_customer_id + DROP TABLE stripe_events. NB: Die Spalte stripe_subscription_id überlebte diese Migration, weil SQLite DROP COLUMN auf UNIQUE-beschränkten Spalten verweigert (per try/catch stillschweigend abgefangen). Siehe Migration 041.
041 rebuild_subscriptions #205 / 2026-05-26 Follow-up zu 040 — baut die Tabelle subscriptions ohne die verwaiste Spalte stripe_subscription_id per CREATE TABLE …-AS-Pattern neu auf. Idempotent (übersprungen, wenn die Spalte bereits fehlt).
042 totp_backup_codes ALTER TABLE users ADD COLUMN totp_backup_codes TEXT — TOTP-Recovery-Codes.
043 worker_avatars Phase D #304 / 2026-05-30 worker_avatars (id PK AUTOINCREMENT, project_name, worker_id, file_path, mime_type, created_at; UNIQUE(project_name, worker_id)) — Metadaten-Store für hochgeladene Worker-Avatar-Bilder. Dateien liegen in data/worker-avatars/. Ausgeliefert über GET /api/crm/projects/:name/workers/:id/avatar.
044 worker_templates Phase I #304 / 2026-05-30 worker_templates (id PK AUTOINCREMENT, owner_id, name, description, config JSON, is_public, created_at; UNIQUE(owner_id, name)) — vom User gespeicherte Worker-Templates. Endpunkte: GET/POST /api/crm/workers/templates, DELETE /api/crm/workers/templates/:id.
045 workers_runtime_state Phase F #306 / 2026-05-31 workers_runtime_state (project_name, worker_id, status, status_started_at, current_skill, current_task_id, updated_at; PK(project_name, worker_id), Index auf updated_at) — Live-Runtime-State pro Worker. Geschrieben von handleSetActiveRole (Invariante: eine aktive Rolle pro Projekt). Gelesen von GET /api/crm/projects/:name/workers/:id/runtime mit Staleness-Fallback: status='working' + tmux tot + updated_at > 10 Min → wird zu idle gezwungen (Crash-Erkennung).
049 embeddings Phase 71.2 #359 / 2026-06-04 embeddings (id PK AUTOINCREMENT, project, doc_type wiki/issue/skill, doc_id, chunk_ix, text, created_at) + embeddings_vec (vec0 Virtual Table, embedding FLOAT[1024]). Trennung: vec0 kann Metadaten nicht effizient halten; LIST/DELETE-Workflows laufen als plain SQL. Die sqlite-vec-Extension wird in initDb automatisch geladen, bevor die Migration läuft.
050 drop_notebook_id Phase 71.8 #365 / 2026-06-05 ALTER TABLE projects DROP COLUMN notebook_id. Entfernt das Schema-Feld der NotebookLM-Bridge; die semantische Suche läuft jetzt über self-hosted RAG (Cohere + sqlite-vec). Idempotent per PRAGMA table_info-Check, damit frische DBs mit aktuellem 001-Schema nicht über die fehlende Spalte stolpern.
051 voice_usage Phase 62.4 #373 / 2026-06-05 voice_usage_log (user_id TEXT, date TEXT YYYY-MM-DD, seconds_used INTEGER DEFAULT 0, PRIMARY KEY(user_id, date)) — täglicher Quota-Zähler für Sprachtranskription, spiegelt die Form von Migration 032 arc_help_usage. Ungefähre Sekunden (geschätzt aus der Upload-Bytegröße bei angenommenen ~32 kbps Voice-Codec); wird vor jedem Transcribe in handleVoiceTranscribe (shared/routes/voice.ts) gegen das weiche Limit von 60 Min/Tag geprüft, damit kein einzelner User den gemeinsamen whisper-server-Worker-Pool auf Contabo monopolisiert.
052 transcripts Phase 73.1 #377 / 2026-06-05 transcripts (id PK AUTOINCREMENT, project TEXT, owner_id TEXT, source_filename TEXT, source_bytes INTEGER, source_kind TEXT CHECK(audio|video), duration_seconds INTEGER, transcript_text TEXT, frames_json TEXT, summary_json TEXT, embed_to_rag INTEGER DEFAULT 1, status TEXT DEFAULT 'queued', error TEXT, created_at TEXT DEFAULT datetime('now'), completed_at TEXT) — eine Zeile pro hochgeladener Meeting-Datei. frames_json = JSON-Array aus {ts_ms, description} von Claude Vision (Phase 73.4). summary_json = {tldr, key_points, action_items, decisions, topics, model, generated_at} von Claude Sonnet (Phase 73.5). Die Quelldatei wird von der Festplatte gelöscht, sobald status='done' (CEO-Entscheidung D4). Indexes auf (project, created_at DESC) und (owner_id, created_at DESC).
056 skill_usage #319 / 2026-06-07 skill_usage (skill_name TEXT, project_name TEXT DEFAULT '_global_', trigger_count INTEGER DEFAULT 0, install_count INTEGER DEFAULT 0, session_count INTEGER DEFAULT 0, last_triggered_at TEXT, updated_at TEXT, PRIMARY KEY(skill_name, project_name)) — kumulative Zähler pro Skill pro Projekt. trigger_count wird inkrementiert, wenn der Context Router den Skill injiziert; install_count bei Save/Create; session_count beim ersten Auftreten in einer Session. Genutzt von GET /api/crm/projects/:name/skills/usage.
057 notes Phase 78.1 #395 / 2026-06-07 4 Tabellen: notes (id PK, project_name, title, description, created_by, created_at, updated_at) — eine Note als Quellen-Container. note_sources (id PK, note_id FK→notes, source_type CHECK(video|audio|youtube|web|pdf|docx|txt|image), filename, file_path, file_size, url, title, content_text, duration_seconds, status CHECK(queued|processing|done|error), error, processed_at, created_at) — eine Quelle pro Zeile, content_text = extrahierter Text für RAG. note_issue_links (note_id FK, issue_id, project_name, linked_at, PK(note_id,issue_id)) — Verknüpfung Note↔Issue. note_chats (id PK, note_id FK→notes, role CHECK(user|assistant), content, created_at) — Chat-Historie der Note. Indexes: notes(project_name,created_at DESC), note_sources(note_id), note_sources(status), note_issue_links(project_name,issue_id), note_chats(note_id,created_at).
053 transcript_jobs Phase 73.1 #377 / 2026-06-05 transcript_jobs (job_id TEXT PK, transcript_id INTEGER, status TEXT DEFAULT 'queued', progress_pct INTEGER DEFAULT 0, step_label TEXT, error TEXT, started_at TEXT DEFAULT datetime('now'), updated_at TEXT DEFAULT datetime('now')) — High-Churn-Fortschrittstracker für den Background-Worker. Von transcripts isoliert, damit häufige UPDATEs (alle paar Sekunden während ffmpeg/whisper) die Indexes der Haupttabelle nicht belasten. Der SSE-Endpoint streamt diese Tabelle mit 1s-Poll. Status-Lifecycle: queued → extracting_audio → transcribing → (video: extracting_frames → frames_extracted → vision_analyzing → vision_analyzed) → summarizing → summarized → embedding → done (oder failed bei jedem Schritt). Index auf (status, updated_at).

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