Banco de Dados — SQLite Schema

Informações Gerais

Diagrama ER (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 tabelas distribuídas em 24 migrações. Auth + projetos + dados do workspace + inteligência + billing + controle de acesso beta + auditoria de exportação de contexto. Todas as constraints de FK são aplicadas.

Tabelas

users — Usuários

Coluna Tipo Descrição
id TEXT PK UUID
email TEXT UNIQUE Email (obrigatório)
password_hash TEXT Hash da senha (bcrypt)
role TEXT 'admin' ou 'user'
name TEXT Nome
avatar_url TEXT URL do avatar
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 Segredo TOTP (2FA)
last_login TEXT ISO 8601
created_at TEXT ISO 8601

projects — Projetos

Coluna Tipo Descrição
id INTEGER PK Auto
technical_name TEXT Nome técnico (único por owner)
owner_id TEXT FK→users Proprietário
display_name TEXT Nome de exibição
description TEXT Descrição
color TEXT Cor (hex)
icon TEXT Ícone
is_archived INTEGER 0/1
sort_order INTEGER Ordem (padrão 999)
notebook_id TEXT NotebookLM ID
project_protocol TEXT Protocolo do projeto
created_at, updated_at TEXT Timestamps

UNIQUE(technical_name, owner_id)

account_settings — Configurações da Conta

Coluna Tipo Descrição
user_id TEXT PK FK→users
anthropic_key TEXT Chave de API Anthropic
openai_key TEXT Chave de API OpenAI
name TEXT Nome
role TEXT Função
auto_harvest INTEGER Coleta automática de skills 0/1

chat_messages — Histórico de Chats

Coluna Tipo Descrição
id INTEGER PK Auto
project_name TEXT Projeto
worker_id TEXT Worker
role TEXT 'user', 'assistant', 'system'
content TEXT Conteúdo da mensagem (criptografado com AES-256-GCM se encrypted=1)
encrypted INTEGER 0=plaintext, 1=vault-encrypted (Phase 45.3)
attachments TEXT Array JSON
timestamp TEXT ISO 8601
metadata TEXT Objeto JSON

INDEX(project_name, worker_id, timestamp DESC)

recovery_keys — Chaves de Recuperação (Phase 45.4)

Coluna Tipo Descrição
id INTEGER PK Auto
user_id TEXT FK→users Proprietário
encrypted_key TEXT Master key criptografada
key_hint TEXT Dica (primeiros 4 caracteres)
created_at TEXT ISO 8601
used_at TEXT Quando foi utilizada
revoked INTEGER 0=ativa, 1=revogada

INDEX(user_id)

github_links — Vínculos de Repositório GitHub (Phase 49.3)

Coluna Tipo Descrição
id INTEGER PK Auto
project_name TEXT Projeto Arc OS
owner TEXT Proprietário do repositório GitHub
repo TEXT Nome do repositório GitHub
webhook_secret TEXT UNIQUE Hex de 32 bytes para validação HMAC-SHA256
created_at TEXT ISO 8601
created_by TEXT FK→users Quem criou o vínculo

UNIQUE(project_name, owner, repo) — múltiplos repositórios por projeto são permitidos. INDEX(project_name), INDEX(webhook_secret)

github_events — Log de Eventos GitHub (Phase 49.3.1)

Coluna Tipo Descrição
id INTEGER PK Auto
link_id INTEGER FK→github_links CASCADE ao excluir vínculo
project_name TEXT Projeto Arc OS (desnormalizado para velocidade de consulta)
event_type TEXT push, pull_request, workflow_run, issues
action TEXT sub-ação (opened/closed/success/failure)
summary TEXT String de exibição pré-formatada
url TEXT Deep link do GitHub
actor TEXT Usuário do GitHub
created_at TEXT ISO 8601

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

skills_global — Skills Globais

Coluna Tipo Descrição
id INTEGER PK Auto
name TEXT UNIQUE Nome
description TEXT Descrição
category TEXT Categoria (padrão 'general')
content TEXT Conteúdo da skill (markdown)
triggers TEXT Array JSON de triggers
keywords TEXT Array JSON de palavras-chave
eval_rules TEXT Array JSON de regras
tool_code_ts TEXT Implementação TypeScript
version INTEGER Versão (auto-increment)
status TEXT 'active', 'draft', 'deprecated', 'archived'

skills_project_forks — Forks de Skills

Coluna Tipo Descrição
id INTEGER PK Auto
project_name TEXT Projeto
skill_id INTEGER FK→skills_global Skill pai (CASCADE)
content TEXT Conteúdo customizado
triggers, keywords, eval_rules, tool_code_ts TEXT Sobrescritas
version INTEGER Versão do fork

UNIQUE(project_name, skill_id)

skill_evolution_logs — Histórico de Alterações de Skills

Coluna Tipo Descrição
id INTEGER PK
skill_id INTEGER FK CASCADE
project_name TEXT Projeto (nullable)
action TEXT 'created', 'modified', 'applied', 'reverted'
diff_summary TEXT Descrição das alterações
author TEXT Autor (padrão 'system')
metadata TEXT JSON

skill_update_requests — PRs de Skills (Sage)

Coluna Tipo Descrição
id INTEGER PK
skill_id INTEGER FK CASCADE
proposed_by TEXT 'sage' (padrão)
status TEXT 'pending', 'approved', 'rejected', 'applied'
current_content TEXT Versão atual
proposed_content TEXT Versão proposta
reason TEXT Motivo da alteração

skill_benchmarks — Testes A/B de Skills

Coluna Tipo Descrição
id INTEGER PK
request_id INTEGER FK→skill_update_requests CASCADE
test_scenario TEXT Cenário de teste
old_output, new_output TEXT Resultados
score_old, score_new REAL Pontuações
judgment_reason TEXT Justificativa

pinned_notes — Notas Fixadas

Coluna Tipo Descrição
id INTEGER PK
project_name TEXT Projeto
worker_id TEXT Worker
title TEXT Título
body TEXT Conteúdo
source_message_id INTEGER Referência ao chat_messages

project_phases — Fases do Roadmap

Coluna Tipo Descrição
id INTEGER PK
project_name TEXT Projeto
phase_id INTEGER Número da fase
phase_title TEXT Nome
status TEXT 'PLANNED', 'ACTIVE', 'COMPLETED', 'CANCELLED'
progress REAL 0.0–1.0
deadline TEXT ISO 8601

UNIQUE(project_name, phase_id)

activity_log — Registro de Atividade

Coluna Tipo Descrição
id INTEGER PK
project_name TEXT Projeto
actor TEXT Worker ou usuário
event_type TEXT Tipo de evento
title TEXT Descrição
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 — Configurações do Sistema

Coluna Tipo Descrição
key TEXT PK Chave
value TEXT Valor
description TEXT Descrição

marketplace_analysis_cache — Cache do Marketplace

Cache de análise de compatibilidade de skills do marketplace.

ephemeral_tokens — Tokens de Vida Curta (issue #27)

Armazenamento persistente para tokens OAuth de uso único, redefinição de senha e verificação de email. Antes da migração 022, esses tokens viviam em um Map em memória e eram perdidos a cada reinício do master, quebrando o login via Google/GitHub (invalid_state).

Coluna Tipo Descrição
token TEXT PK Hex de 32 bytes (randomBytes(32))
type TEXT NOT NULL oauth_state | password_reset | email_verification
payload TEXT NOT NULL JSON: {provider} para oauth_state, {email} para os demais
expires_at INTEGER NOT NULL Época em ms — TTL: 10 min (oauth) / 30 min (reset) / 24h (verify)

INDEX(type), INDEX(expires_at). Uso único: consume() em uma única transação lê o payload e exclui o registro.

project_issues — Issues por projeto (issue #53, Phase 53.14)

SQLite como SSOT para issues, substituindo o issues/issues.json por projeto. O arquivo continua em disco como export derivado (mirror), ignorado pelo git — o DB é autoritativo.

Coluna Tipo Descrição
project_name TEXT NOT NULL nome do registry; parte da PK composta com id
id INTEGER NOT NULL sequência 1-based por projeto (max+1 no insert)
title TEXT NOT NULL título da issue
body TEXT NOT NULL DEFAULT '' descrição, markdown
priority TEXT NOT NULL DEFAULT 'P2' P0 | P1 | P2 | P3
labels TEXT NOT NULL DEFAULT '[]' Array JSON de strings
status TEXT NOT NULL DEFAULT 'open' open | closed
created_at TEXT NOT NULL Timestamp ISO
updated_at TEXT NOT NULL Timestamp ISO
closed_at TEXT NULL Timestamp ISO quando status=closed
activity TEXT NOT NULL DEFAULT '[]' Array JSON {ts, type, author, text} — auditoria append-only

PK (project_name, id) — cada projeto tem seu próprio espaço de IDs independente. INDEX (project_name, status) — filtro mais frequente (open only). As operações ficam em issueQueries (shared/db.ts): list/get/nextId/insert/upsert/replaceAll/bulkImport. replaceAll — transação atômica; bulkImportINSERT OR IGNORE para re-seed idempotente.

onboarding_progress — Estado do checklist pós-wizard (issue #56, Phase 54.1)

Cache derivado por usuário sobre o stream de eventos do activity_log (SSOT). A UI lê com uma única consulta em vez de agregar eventos. Replayável: dismiss/replay não alteram state, apenas dismissed_at.

Coluna Tipo Descrição
chat_id TEXT PRIMARY KEY id do usuário (chat id do registry)
state TEXT NOT NULL DEFAULT '{}' JSON {workers, cli, skill, bot, issue}completed | skipped (chaves ausentes = pending)
completed_count INTEGER NOT NULL DEFAULT 0 cache derivado com contagem de steps completed (0–5)
started_at TEXT NOT NULL DEFAULT (datetime('now')) criação do registro (primeiro evento/dismiss/replay)
completed_at TEXT NULL Timestamp ISO quando todos os 5 steps estão completed
dismissed_at TEXT NULL Timestamp ISO quando o usuário fechou o checklist
source TEXT NOT NULL DEFAULT 'web' web | cli — para atribuição de funil (#58 arc tour)
updated_at TEXT NOT NULL DEFAULT (datetime('now')) timestamp de mutação

INDEX em completed_at para análise de funil (#61 Phase 54.6). As operações ficam em onboardingQueries (shared/db.ts): getProgress/recordEvent/dismiss/replay. recordEvent é idempotente em (chat_id, step, status) — uma chamada idêntica repetida retorna changed=false e não escreve no activity_log. Whitelist: 5 steps × 2 statuses (completed, skipped). skipped NÃO incrementa completed_count. A transição skipped → completed adiciona ao count. Todas as mutações emitem eventos no activity_log com event_type LIKE 'onboarding_%' — verdadeiro SSOT para métricas de funil; as colunas da tabela são cache derivado.

platform_audit_log — Trilha de rotação de segredos para super-admin (Phase 57, Sentinel #103)

Registro append-only de mutações em vault platform-secrets via UI de Configurações da Plataforma. SSOT para forense pós-incidente ("qual admin rotacionou a chave Anthropic às 03:14 UTC?"). O próprio vault.json não registra isso — apenas o valor.

Coluna Tipo Descrição
id INTEGER PRIMARY KEY AUTOINCREMENT ordem temporal mesmo com clock skew
ts TEXT NOT NULL DEFAULT (datetime('now')) UTC server-side; atacante não pode retroagir
user_chat_id TEXT NOT NULL admin que executou a ação
user_email TEXT NULL snapshot da tabela users no momento da ação
action TEXT NOT NULL list | view | rotate | test | restart
key_name TEXT NOT NULL nome da entrada no vault (allowlist em platform.ts); * para list-all
ip TEXT NULL de CF-Connecting-IP / X-Real-IP / XFF tail
result TEXT NOT NULL success ou fail:<reason>
user_agent TEXT NULL string do UA, truncada em 200 chars

INDEXES: idx_platform_audit_ts (recentes por chave), idx_platform_audit_user (trilha por admin), idx_platform_audit_key (histórico de rotação por chave). Invariante append-only: sem handlers de UPDATE/DELETE; a UI expõe somente leitura via platformAuditQueries.recent + lastRotated em shared/db.ts. Cada ação (sucesso + falha) grava um registro.

Migrações

# Nome Fase O que adiciona
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 em mensagens de worker
014 timeline_events Phase 47 tabela timeline_events
015 e2ee_chat Phase 45.3 +coluna encrypted em chat_messages
016 recovery_keys Phase 45.4 tabela recovery_keys
017 github_links Phase 49.3 tabela github_links (vínculos de webhook)
018 github_events Phase 49.3.1 tabela github_events (log de eventos para feed da UI)
019 trial_credits Phase 50.1 +users.trial_granted_at, +projects.trial_mode, +projects.trial_tokens_remaining
020 subscriptions Phase 51 tabela subscriptions (plan, stripe_customer_id, status, feature_flags) + log de idempotência stripe_events
021 invites Phase 52.1 tabela invites (cadastro beta fechado) — code, created_by, used_by, parent_invite_code, status, note
022 ephemeral_tokens issue #27 tabela ephemeral_tokens — OAuth state / redefinição de senha / verificação de email persistentes, substituindo Map em memória (corrige invalid_state no reinício do master)
023 project_issues Phase 53.14 / issue #53 tabela project_issues (PK composta project_name+id, JSON labels/activity) — migra o armazenamento de issues do issues/issues.json para o SQLite como SSOT; o JSON passa a ser mirror derivado; elimina drift de merge de texto no 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) — histórico de auditoria por projeto para o Project Context Export + toggles de política por projeto; alerta quando ≥3 exports/24h com notify_on_export = true
025 onboarding_progress Phase 54.1 / issue #56 onboarding_progress (chat_id PK, JSON state por 5 steps, completed_count derivado, started_at/completed_at/dismissed_at, source web|cli) — cache derivado sobre stream de eventos do activity_log; SSOT = eventos event_type LIKE 'onboarding_%'; recordEvent idempotente, dismiss/replay não-destrutivos
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) — trilha de auditoria append-only para mutações da UI de Configurações da Plataforma (super-admin) em vault secrets. 3 indexes: idx_platform_audit_ts/user/key. Sem UPDATE/DELETE — somente leitura via 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) — Log de eventos de autenticação + IP append-only. Eventos: signup/login/oauth_login/magic_link/device_code_approve. Resultados: success/failed/rate_limited/invalid_invite/already_registered/2fa_required/invalid_token. 4 índices: ts/user_id/ip/event+result
028 managed_containers Phase 60 #135/#136 managed_containers (id TEXT PK (nome do contêiner Docker), user_id, status (provisioning|ready|paused|suspended|deleted), server_ip, internal_port INT, claude_authed INT, github_authed INT, created_at, last_active) — Registro de contêineres Docker 1-por-usuário para Standard Cloud. managedContainerQueries.findByUser/insert/updateStatus/setAuthFlag/touch/listActive em shared/db.ts. Endpoints: POST /api/crm/cloud/provision, GET /status, POST /deprovision em 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) — Lista de espera Early Access para Standard Cloud. cloudWaitlistQueries.join/findByUser/list/invite/activate/decline/stats em shared/db.ts. Endpoints: POST /api/crm/cloud/waitlist (entrar), GET /waitlist/status (próprio status), GET /waitlist (lista do admin), POST /waitlist/invite (admin ativa + faz upgrade do plano para 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) — Feedback de qualidade de tradução por locale. Painel de revisão do admin em /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())) — Log de uso de tokens Claude por requisição. 3 índices: owner_id, project_name, created_at. Preenchido por child-bot/claude-runner.ts via POST /api/internal/usage/log (fire-and-forget). Consultado por GET /api/crm/account/usage → retorna { rows: [...últimos 200], totals: { total, input, output } } para 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)) — Contador de rate-limit diário para o chat de Ajuda IA no app (30 mensagens/dia). Consultado por GET /api/crm/help/usage; atualizado em POST /api/crm/help/chat.

Relacionamentos (Foreign Keys)

Todos com CASCADE — excluir uma skill exclui todos os seus forks, histórico, PRs e benchmarks.

Diagrama ER (textual)

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