База даних — SQLite Schema
Загальна інформація
- Файл:
data/citadel.db - Режим: WAL (Write-Ahead Logging) — паралельне читання
- Foreign keys: увімкнені
- Busy timeout: 5000ms
- Cache: 64MB
- Міграції:
shared/migrations/001-032
ER Diagram (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 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
}
32 таблиці у 32 міграціях. Auth + проєкти + workspace-дані + intelligence + білінг + beta gating + cloud + token tracking. Усі FK-обмеження активні.
Таблиці
users — Користувачі
| Колонка | Тип | Опис |
|---|---|---|
| id | TEXT PK | UUID |
| TEXT UNIQUE | Email (обов'язковий) | |
| password_hash | TEXT | Хеш пароля (bcrypt) |
| role | TEXT | 'admin' або 'user' |
| name | TEXT | Ім'я |
| avatar_url | TEXT | 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-секрет (2FA) |
| last_login | TEXT | ISO 8601 |
| created_at | TEXT | ISO 8601 |
projects — Проєкти
| Колонка | Тип | Опис |
|---|---|---|
| id | INTEGER PK | Auto |
| technical_name | TEXT | Технічне ім'я (унікальне per owner) |
| owner_id | TEXT FK→users | Власник |
| display_name | TEXT | Відображуване ім'я |
| description | TEXT | Опис |
| color | TEXT | Колір (hex) |
| icon | TEXT | Іконка |
| is_archived | INTEGER | 0/1 |
| sort_order | INTEGER | Порядок (default 999) |
| notebook_id | TEXT | NotebookLM ID |
| project_protocol | TEXT | Протокол проєкту |
| created_at, updated_at | TEXT | Timestamps |
UNIQUE(technical_name, owner_id)
account_settings — Налаштування акаунту
| Колонка | Тип | Опис |
|---|---|---|
| user_id | TEXT PK FK→users | |
| anthropic_key | TEXT | Anthropic API ключ |
| openai_key | TEXT | OpenAI API ключ |
| name | TEXT | Ім'я |
| role | TEXT | Роль |
| auto_harvest | INTEGER | Автозбір навичок 0/1 |
chat_messages — Історія чатів
| Колонка | Тип | Опис |
|---|---|---|
| id | INTEGER PK | Auto |
| project_name | TEXT | Проєкт |
| worker_id | TEXT | Воркер |
| role | TEXT | 'user', 'assistant', 'system' |
| content | TEXT | Текст повідомлення (зашифровано AES-256-GCM, якщо encrypted=1) |
| encrypted | INTEGER | 0=plaintext, 1=vault-encrypted (Phase 45.3) |
| attachments | TEXT | JSON-масив |
| timestamp | TEXT | ISO 8601 |
| metadata | TEXT | JSON-об'єкт |
INDEX(project_name, worker_id, timestamp DESC)
recovery_keys — Ключі відновлення (Phase 45.4)
| Колонка | Тип | Опис |
|---|---|---|
| id | INTEGER PK | Auto |
| user_id | TEXT FK→users | Власник |
| encrypted_key | TEXT | Зашифрований master key |
| key_hint | TEXT | Підказка (перші 4 символи) |
| created_at | TEXT | ISO 8601 |
| used_at | TEXT | Коли використано |
| revoked | INTEGER | 0=активний, 1=відкликаний |
INDEX(user_id)
github_links — Прив'язки GitHub repo (Phase 49.3)
| Колонка | Тип | Опис |
|---|---|---|
| id | INTEGER PK | Auto |
| project_name | TEXT | Arc OS project |
| owner | TEXT | GitHub repo owner |
| repo | TEXT | GitHub repo name |
| webhook_secret | TEXT UNIQUE | 32-byte hex для HMAC-SHA256 валідації |
| created_at | TEXT | ISO 8601 |
| created_by | TEXT FK→users | Хто створив link |
UNIQUE(project_name, owner, repo) — multi-repo на проєкт дозволено. INDEX(project_name), INDEX(webhook_secret)
github_events — Лог GitHub-подій (Phase 49.3.1)
| Колонка | Тип | Опис |
|---|---|---|
| id | INTEGER PK | Auto |
| link_id | INTEGER FK→github_links | CASCADE при видаленні link |
| project_name | TEXT | Arc OS project (денормалізовано для швидкості запитів) |
| event_type | TEXT | push, pull_request, workflow_run, issues |
| action | TEXT | під-дія (opened/closed/success/failure) |
| summary | TEXT | Готовий рядок для відображення |
| url | TEXT | GitHub deep link |
| actor | TEXT | GitHub username |
| created_at | TEXT | ISO 8601 |
INDEX(project_name, created_at DESC), INDEX(link_id)
skills_global — Глобальні навички
| Колонка | Тип | Опис |
|---|---|---|
| id | INTEGER PK | Auto |
| name | TEXT UNIQUE | Назва |
| description | TEXT | Опис |
| category | TEXT | Категорія (default 'general') |
| content | TEXT | Вміст навички (markdown) |
| triggers | TEXT | JSON-масив тригерів |
| keywords | TEXT | JSON-масив ключових слів |
| eval_rules | TEXT | JSON-масив правил |
| tool_code_ts | TEXT | TypeScript-реалізація |
| version | INTEGER | Версія (auto-increment) |
| status | TEXT | 'active', 'draft', 'deprecated', 'archived' |
skills_project_forks — Форки навичок
| Колонка | Тип | Опис |
|---|---|---|
| id | INTEGER PK | Auto |
| project_name | TEXT | Проєкт |
| skill_id | INTEGER FK→skills_global | Батьківська навичка (CASCADE) |
| content | TEXT | Кастомізований вміст |
| triggers, keywords, eval_rules, tool_code_ts | TEXT | Перевизначення |
| version | INTEGER | Версія форку |
UNIQUE(project_name, skill_id)
skill_evolution_logs — Історія змін навичок
| Колонка | Тип | Опис |
|---|---|---|
| id | INTEGER PK | |
| skill_id | INTEGER FK | CASCADE |
| project_name | TEXT | Проєкт (nullable) |
| action | TEXT | 'created', 'modified', 'applied', 'reverted' |
| diff_summary | TEXT | Опис змін |
| author | TEXT | Автор (default 'system') |
| metadata | TEXT | JSON |
skill_update_requests — PR навичок (Sage)
| Колонка | Тип | Опис |
|---|---|---|
| id | INTEGER PK | |
| skill_id | INTEGER FK | CASCADE |
| proposed_by | TEXT | 'sage' (default) |
| status | TEXT | 'pending', 'approved', 'rejected', 'applied' |
| current_content | TEXT | Поточна версія |
| proposed_content | TEXT | Запропонована версія |
| reason | TEXT | Причина зміни |
skill_benchmarks — A/B-тести навичок
| Колонка | Тип | Опис |
|---|---|---|
| id | INTEGER PK | |
| request_id | INTEGER FK→skill_update_requests | CASCADE |
| test_scenario | TEXT | Сценарій тесту |
| old_output, new_output | TEXT | Результати |
| score_old, score_new | REAL | Оцінки |
| judgment_reason | TEXT | Обґрунтування |
pinned_notes — Закріплені нотатки
| Колонка | Тип | Опис |
|---|---|---|
| id | INTEGER PK | |
| project_name | TEXT | Проєкт |
| worker_id | TEXT | Воркер |
| title | TEXT | Заголовок |
| body | TEXT | Текст |
| source_message_id | INTEGER | Посилання на chat_messages |
project_phases — Фази Roadmap
| Колонка | Тип | Опис |
|---|---|---|
| id | INTEGER PK | |
| project_name | TEXT | Проєкт |
| phase_id | INTEGER | Номер фази |
| phase_title | TEXT | Назва |
| status | TEXT | 'PLANNED', 'ACTIVE', 'COMPLETED', 'CANCELLED' |
| progress | REAL | 0.0–1.0 |
| deadline | TEXT | ISO 8601 |
UNIQUE(project_name, phase_id)
activity_log — Журнал активності
| Колонка | Тип | Опис |
|---|---|---|
| id | INTEGER PK | |
| project_name | TEXT | Проєкт |
| actor | TEXT | Воркер або користувач |
| event_type | TEXT | Тип події |
| title | TEXT | Опис |
| 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 — Системні налаштування
| Колонка | Тип | Опис |
|---|---|---|
| key | TEXT PK | Ключ |
| value | TEXT | Значення |
| description | TEXT | Опис |
marketplace_analysis_cache — Кеш маркетплейсу
Кеш аналізу сумісності навичок з маркетплейсу.
ephemeral_tokens — Короткоживучі токени (issue #27)
Persistent-сховище для одноразових токенів OAuth state, password reset та email verification. До міграції 022 ці токени жили в in-memory Map і губилися при кожному рестарті master, ламаючи Google/GitHub login (invalid_state).
| Колонка | Тип | Опис |
|---|---|---|
| token | TEXT PK | 32-байтний hex (randomBytes(32)) |
| type | TEXT NOT NULL | oauth_state | password_reset | email_verification |
| payload | TEXT NOT NULL | JSON: {provider} для oauth_state, {email} для решти |
| expires_at | INTEGER NOT NULL | ms epoch — TTL: 10 хв (oauth) / 30 хв (reset) / 24 год (verify) |
INDEX(type), INDEX(expires_at). Одноразове використання: consume() в одній транзакції читає payload і видаляє рядок.
Міграції
| # | Назва | Фаза | Що додає |
|---|---|---|---|
| 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 на повідомленнях воркерів |
| 014 | timeline_events | Phase 47 | таблиця timeline_events |
| 015 | e2ee_chat | Phase 45.3 | +колонка encrypted у chat_messages |
| 016 | recovery_keys | Phase 45.4 | таблиця recovery_keys |
| 017 | github_links | Phase 49.3 | таблиця github_links (webhook bindings) |
| 018 | github_events | Phase 49.3.1 | таблиця github_events (event log для UI feed) |
| 019 | trial_credits | Phase 50.1 | +users.trial_granted_at, +projects.trial_mode, +projects.trial_tokens_remaining |
| 020 | subscriptions | Phase 51 | таблиця subscriptions (plan, stripe_customer_id, status, feature_flags) + лог ідемпотентності stripe_events |
| 021 | invites | Phase 52.1 | таблиця invites (closed beta enrollment) — code, created_by, used_by, parent_invite_code, status, note |
| 022 | ephemeral_tokens | issue #27 | таблиця ephemeral_tokens — persistent OAuth state / password reset / email verification замість in-memory Map (фікс invalid_state на рестарті master) |
| 023 | project_issues | Phase 53.14 / issue #53 | таблиця project_issues (composite PK project_name+id, JSON labels/activity) — переносить issues storage з issues/issues.json у SQLite SSOT, JSON стає derived mirror; ліквідовує text-merge drift на 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) — за-проєктна audit history для Project Context Export + per-project policy toggles; alert при ≥3 exports/24h коли notify_on_export = true |
| 025 | onboarding_progress | Phase 54.1 / issue #56 | onboarding_progress (chat_id PK, JSON state per 5 steps, completed_count derived, started_at/completed_at/dismissed_at, source web|cli) — derived cache над activity_log event stream; SSOT = події event_type LIKE 'onboarding_%'; recordEvent ідемпотентний, dismiss/replay non-destructive |
| 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 для super-admin Platform Settings UI мутацій vault secrets. 3 indexes: idx_platform_audit_ts/user/key. No UPDATE/DELETE — read-only через 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 подій. Events: signup/login/oauth_login/magic_link/device_code_approve. Results: 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) — реєстр Docker-контейнерів 1-на-користувача для Standard Cloud. managedContainerQueries.findByUser/insert/updateStatus/setAuthFlag/touch/listActive у shared/db.ts. Endpoints: POST /api/crm/cloud/provision, GET /status, POST /deprovision у 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 для Standard Cloud. cloudWaitlistQueries.join/findByUser/list/invite/activate/decline/stats у shared/db.ts. Endpoints: POST /api/crm/cloud/waitlist (вступити), GET /waitlist/status (власний статус), GET /waitlist (список для admin), POST /waitlist/invite (admin активує + upgrade план до 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 feedback якості перекладів. Admin review dashboard на /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. 3 indexes: owner_id, project_name, created_at. Заповнюється з child-bot/claude-runner.ts через POST /api/internal/usage/log (fire-and-forget). Запитується через GET /api/crm/account/usage → повертає { rows: [...останні 200], totals: { total, input, output } } для 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)) — Щоденний лічильник rate-limit для вбудованого чату AI-допомоги (30 повідомлень/день). Запитується через GET /api/crm/help/usage; оновлюється при POST /api/crm/help/chat. |
Зв'язки (Foreign Keys)
- skills_project_forks.skill_id → skills_global.id (CASCADE DELETE)
- skill_evolution_logs.skill_id → skills_global.id (CASCADE DELETE)
- skill_update_requests.skill_id → skills_global.id (CASCADE DELETE)
- skill_benchmarks.request_id → skill_update_requests.id (CASCADE DELETE)
Усі CASCADE — видалення навички видаляє всі форки, історію, PR та бенчмарки.
ER-діаграма (текстова)
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