База данных — SQLite Schema

Общая информация

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
    }

23 таблицы в 24 миграциях. Auth + проекты + данные workspace + интеллект-слой + billing + бета-gating + аудит контекстного экспорта. Все FK-ограничения соблюдены.

Таблицы

users — Пользователи

Колонка Тип Описание
id TEXT PK UUID
email 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-байтный hex для HMAC-SHA256 validation
created_at TEXT ISO 8601
created_by TEXT FK→users Кто создал link

UNIQUE(project_name, owner, repo) — multi-repo per project allowed. INDEX(project_name), INDEX(webhook_secret)

github_events — Лог GitHub-событий (Phase 49.3.1)

Колонка Тип Описание
id INTEGER PK Auto
link_id INTEGER FK→github_links CASCADE on link delete
project_name TEXT Arc OS project (denormalized для query speed)
event_type TEXT push, pull_request, workflow_run, issues
action TEXT sub-action (opened/closed/success/failure)
summary TEXT Pre-formatted display string
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 — Кеш marketplace

Кеш анализа совместимости скилов из marketplace.

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 и удаляет строку.

project_issues — Задачи per project (issue #53, Phase 53.14)

SQLite SSOT для задач вместо per-project issues/issues.json. Файл остаётся на диске как derived export (mirror), gitignored — DB авторитативна.

Колонка Тип Описание
project_name TEXT NOT NULL name из registry; составная PK вместе с id
id INTEGER NOT NULL 1-based per-project sequence (max+1 при insert)
title TEXT NOT NULL заголовок задачи
body TEXT NOT NULL DEFAULT '' описание, 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 при status=closed
activity TEXT NOT NULL DEFAULT '[]' JSON array {ts, type, author, text} — append-only audit

PK (project_name, id) — у каждого проекта свой независимый id-пространство. INDEX (project_name, status) — самый частый фильтр (open only). Operations live in issueQueries (shared/db.ts): list/get/nextId/insert/upsert/replaceAll/bulkImport. replaceAll — атомарная транзакция; bulkImportINSERT OR IGNORE для idempotent re-seed.

onboarding_progress — Состояние post-wizard чеклиста (issue #56, Phase 54.1)

Per-user derived cache над event stream из activity_log (SSOT). UI читает одним запросом вместо агрегации по событиям. Replayable: dismiss/replay не меняют state, только dismissed_at.

Колонка Тип Описание
chat_id TEXT PRIMARY KEY user id (registry chat id)
state TEXT NOT NULL DEFAULT '{}' JSON {workers, cli, skill, bot, issue}completed | skipped (отсутствующие ключи = pending)
completed_count INTEGER NOT NULL DEFAULT 0 derived cache count of completed steps (0–5)
started_at TEXT NOT NULL DEFAULT (datetime('now')) создание строки (первое событие/dismiss/replay)
completed_at TEXT NULL ISO timestamp когда все 5 steps completed
dismissed_at TEXT NULL ISO timestamp когда пользователь закрыл чеклист
source TEXT NOT NULL DEFAULT 'web' web | cli — для funnel attribution (#58 arc tour)
updated_at TEXT NOT NULL DEFAULT (datetime('now')) mutation timestamp

INDEX на completed_at для funnel-аналитики (#61 Phase 54.6). Operations live in onboardingQueries (shared/db.ts): getProgress/recordEvent/dismiss/replay. recordEvent идемпотентен на (chat_id, step, status) — повторный идентичный вызов возвращает changed=false и не пишет в activity_log. Whitelist: 5 steps × 2 statuses (completed, skipped). skipped НЕ инкрементирует completed_count. Переход skipped → completed добавляет к count. Все мутации эмитируют события в activity_log с event_type LIKE 'onboarding_%' — настоящий SSOT для funnel-метрик; колонки таблицы — derived cache.

platform_audit_log — Super-admin trail ротации секретов (Phase 57, Sentinel #103)

Append-only журнал мутаций platform-secrets через vault в Platform Settings UI. SSOT для post-incident forensics («какой admin ротировал Anthropic key в 03:14 UTC?»). Vault.json сам этого не пишет — только значения.

Колонка Тип Описание
id INTEGER PRIMARY KEY AUTOINCREMENT временной порядок даже при clock skew
ts TEXT NOT NULL DEFAULT (datetime('now')) server-side UTC; attacker не может backdating
user_chat_id TEXT NOT NULL admin, выполнивший действие
user_email TEXT NULL snapshot из таблицы users на момент действия
action TEXT NOT NULL list | view | rotate | test | restart
key_name TEXT NOT NULL имя vault-записи (allowlist в platform.ts); * для list-all
ip TEXT NULL из CF-Connecting-IP / X-Real-IP / XFF tail
result TEXT NOT NULL success или fail:<reason>
user_agent TEXT NULL UA-строка, truncated 200 chars

INDEXES: idx_platform_audit_ts (recent across keys), idx_platform_audit_user (per-admin trail), idx_platform_audit_key (история ротаций per key). Инвариант append-only: нет UPDATE/DELETE-обработчиков; UI предоставляет read-only recent() + lastRotated() через platformAuditQueries в shared/db.ts. Каждое действие (success+fail) записывает строку.

Миграции

# Название Фаза Что добавляет
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 (лог событий для 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 idempotency log
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/issues.json в SQLite SSOT, JSON становится derived mirror; устраняет text-merge drift при деплое
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) — per-project история аудита для Project Context Export + per-project policy toggles; алерт при ≥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 над event stream из activity_log; 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. Нет 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. События: signup/login/oauth_login/magic_link/device_code_approve. Результаты: success/failed/rate_limited/invalid_invite/already_registered/2fa_required/invalid_token. 4 индекса: ts/user_id/ip/event+result
028 managed_containers Phase 60 #135/#136 managed_containers (id TEXT PK (имя Docker-контейнера), 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 (список для admins), POST /waitlist/invite (admin активирует + апгрейд плана до 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) — Обратная связь о качестве переводов по locale. Дашборд проверки для admin на /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())) — Лог использования токенов Claude на каждый запрос. 3 индекса: 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)

Все 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