Base de datos workspace · arquitectura técnica
Referencia técnica del esquema D1 del workspace. Para una explicación accesible ve a [[workspace-tech—bd—coloquial]].
1. Stack de persistencia
| Componente | Versión | Rol |
|---|---|---|
| Cloudflare D1 | latest | BD principal · SQLite replicado en edge |
| Cloudflare R2 | latest | Object storage (blobs · imágenes/PDFs adjuntos a tasks) |
| Cloudflare Vectorize | latest | Vector store para embeddings (Bibliotecario search semántico) |
| Wrangler | 4.85+ | CLI de migrations + deploy + import/export |
| CF Pages Functions | — | Runtime que ejecuta los handlers que consultan D1 |
Bindings (wrangler.toml)
[[d1_databases]]
binding = "DB"
database_name = "crearacksl-workspace-db"
database_id = "<uuid>"
[[r2_buckets]]
binding = "R2"
bucket_name = "crearacksl-workspace-blobs"
[[vectorize]]
binding = "VECTORIZE"
index_name = "bib-chunks-embeddings"
Limitaciones D1 documentadas (footguns)
| Límite | Impacto | Workaround |
|---|---|---|
| 100 bind params max por query | WHERE id IN (?,?,...) con más rompe | Chunk a 90 vía db.batch() |
| Worker subrequests | Loop con SELECT + INSERT por iteración excede límite | db.batch() + INSERT ON CONFLICT + pre-fetch IN chunks |
| CHECK constraints laxos | Algunos INSERT path no validan CHECK | Validar también en aplicación |
datetime() + bind concat | `datetime(‘now’,’-’ |
2. Migrations · estado actual
30 migrations aplicadas (migrations/ en el repo workspace):
| # | Nombre | Dominio | Notas |
|---|---|---|---|
| 0001 | create_news | dashboard | Anuncios |
| 0002 | create_tasks | tasks | Tablero principal |
| 0003 | create_notes | dashboard | Post-its colores |
| 0004 | create_alerts | dashboard | Banner home |
| 0005 | create_auth_codes | auth | Magic link nonces |
| 0006 | create_activity_log | tools | Bitácora MCP |
| 0007 | create_reports | dashboard | Informes IA |
| 0008 | create_biblioteca | supercontext | 8 tablas bib_* base |
| 0009 | create_bib_chunks | supercontext | Chunks para semantic search |
| 0010 | biblioteca_graphify | supercontext | bib_communities + confidence en edges |
| 0011 | create_task_attachments | tasks | Adjuntos R2 |
| 0012 | tasks_transition_timestamps | tasks | Timestamps de transición |
| 0013 | tasks_resolution | tasks | Resolución/cierre |
| 0014 | create_bib_wiki | supercontext | 4 tablas wiki Supercontexto |
| 0015 | bib_wiki_log_allow_delete | supercontext | Fix CHECK constraint |
| 0016 | create_ai_eval_runs | tools | Historial /tools/ai-eval |
| 0017 | create_quick_links | herramientas-personales | Hub URLs |
| 0018 | seed_quick_links | herramientas-personales | Seed inicial |
| 0019 | quicklinks_tailnet_update | herramientas-personales | Update URLs tailnet |
| 0020 | create_directions | herramientas-personales | Agenda contactos |
| 0021 | seed_directions | herramientas-personales | Seed |
| 0022 | create_notebook | herramientas-personales | Bloc markdown |
| 0023 | tasks_linked_notes | tasks | Vínculo task ↔ note |
| 0024 | kind_lifecycle_drift | supercontext | Schema wiki extension |
| 0025 | quick_links_unique | herramientas-personales | UNIQUE constraint (fix footgun #d1_migrations_untracked) |
| 0026 | create_zoho_oauth | oauth | Singleton tokens Edu |
| 0027 | zoho_per_user_and_task_links | oauth + tasks | Multi-user + vínculos Calendar/Mail |
| 0028 | task_calendar_link_type | tasks | Tipo de link |
| 0029 | create_task_comments | tasks | Comentarios lineales (s69) |
| 0030 | create_chat | chat | Chat grupal equipo (s69) |
Aplicar migrations
# Local dev
pnpm wrangler d1 execute crearacksl-workspace-db --local --file=./migrations/0030_create_chat.sql
# Producción
pnpm wrangler d1 migrations apply crearacksl-workspace-db --remote
Footgun s64: migrations aplicadas con
execute --filedirecto NO quedan end1_migrationstable. Unmigrations applyposterior las re-aplica silenciosamente. Causó duplicado de 27 quick_links. Solución: usar siempremigrations applypara migrations versionadas + UNIQUE constraints donde haga falta.Desde el 11-09-2026 (s323) nadie aplica migraciones a mano: las aplica el deploy de OPS (
cf-pages-deploy.sh) antes de publicar, con candado contra el registro desincronizado (reincidencia del 11-09: 0045-0053 sin registrar,migrations applyreventó en la 0045). Flujo: PR con el.sqlenmigrations/+ su línea enscripts/seed-d1-migrations.sql→ merge → el tick del deploy la aplica. Detalle: [[ia-tech—automatismos—metodo—cf-pages-deploy]].
3. Mapa ER por dominios
3.1 Dashboard del home
erDiagram
news {
int id PK
string title
string content
string author
string category
int pinned
string created_at
string updated_at
}
notes {
int id PK
string title
string content
string color "blue|green|yellow|pink|orange"
string author
string created_at
string updated_at
}
alerts {
int id PK
string message
string type "info|warning|critical"
int active
string author
string created_at
}
reports {
int id PK
string type
string title
string data "JSON"
string ai_analysis
string period
string created_at
}
Tablas independientes, sin FKs. CRUD simple desde React + Workers.
3.2 Tasks ecosystem
erDiagram
tasks ||--o{ task_attachments : "tiene"
tasks ||--o{ task_comments : "tiene"
tasks ||--o{ task_calendar_events : "vinculado a"
tasks ||--o{ task_emails : "vinculado a"
tasks {
int id PK
string title
string description
string assignee "Edu|Dani|Txell|Equipo"
string status "pending|in_progress|completed|deleted"
string priority "low|normal|high|urgent"
string due_date
string created_by
string created_at
string updated_at
}
task_attachments {
int id PK
int task_id FK
string r2_key UK "tasks/{id}/{uuid}.{ext}"
string filename
string content_type
int size_bytes
string uploaded_by
string uploaded_at
}
task_comments {
int id PK
int task_id FK
string author
string body
string target_user_ids "JSON array"
string created_at
}
task_calendar_events {
int task_id PK
string zoho_event_uid PK
string calendar_uid
string title
string start_at
string end_at
string url
string created_by
string created_at
}
task_emails {
int task_id PK
string zoho_message_id PK
string thread_id
string subject
string from_address
string email_date
string url
string linked_by
string linked_at
}
Único bloque con FKs reales (
ON DELETE CASCADE). Borrar una task limpia attachments, comments, calendar links y email links en cascada. Los blobs R2 los elimina el endpoint DELETE manualmente.
3.3 Herramientas personales (scope user)
erDiagram
notebook_notes {
int id PK
string owner_id "email CF Access"
string title
string content "JSON TipTap"
int word_count
int pinned
string archived_at
string created_at
string updated_at
}
directions {
int id PK
string type "person|company"
string name
string nif
string company
string role
string email
string phone
string linkedin
string street
string zip
string city
string province
string country
string tags "CSV"
string notes
string archived_at
string owner_id
string created_at
string updated_at
}
quick_links {
int id PK
string label
string url
string description
string category "infrastructure|code|ai|monitoring|product|selfhosted|admin|general"
string icon_url
int position
int is_shared "1=team|0=private"
string owner_id
string archived_at
string created_at
string updated_at
}
Patrón común: owner_id (email CF Access) + archived_at para soft-delete.
3.4 OAuth Zoho · evolución per-user
erDiagram
zoho_oauth_tokens {
string team_member PK "CHECK Edu|Dani|Txell"
string zoho_email
string refresh_token
string access_token
string access_token_expires_at
string dc "default eu"
string granted_by_cf_email
string granted_at
string updated_at
}
zoho_oauth_state {
string state PK
string created_by
string created_at
}
Evolución:
- s67 (0026): singleton (
CHECK id = 1) con un solo refresh token del “equipo” - s68 pivot (0027): migrado a per-user,
team_membercomo PK, preservando token de Edu
3.5 Supercontexto · grafo + wiki
Es el dominio más rico del workspace. Conceptualmente:
erDiagram
bib_nodes ||--o{ bib_edges : "source"
bib_nodes ||--o{ bib_edges : "target"
bib_nodes ||--o| bib_docs : "metadata doc"
bib_nodes ||--o| bib_endpoints : "metadata endpoint"
bib_nodes ||--o{ bib_chunks : "chunks para search"
bib_nodes ||--o{ bib_change_log : "auditoría"
bib_nodes }o--o| bib_communities : "cluster"
bib_index_runs ||--o{ bib_change_log : "registra"
bib_wiki_pages ||--o{ bib_wiki_log : "operaciones"
bib_wiki_pages ||--o{ bib_wiki_utility : "eventos uso"
bib_wiki_pages }o--o| bib_communities : "cluster"
bib_wiki_pages }o--o{ bib_wiki_contradictions : "página A"
bib_wiki_pages }o--o{ bib_wiki_contradictions : "página B"
Tablas (~13):
| Tabla | Propósito | Schema clave |
|---|---|---|
bib_nodes | Nodos del grafo (endpoint, model, service, signal, schema, doc, view, template, js_module, function, class, method, module) | qualified_name UNIQUE, node_type, app, file_path, content_hash, community_id |
bib_edges | Relaciones tipadas | source_id FK, target_id FK, edge_type, weight, confidence (EXTRACTED/INFERRED), confidence_score |
bib_docs | Metadata extendida para nodes doc | title, category, doc_type, sections JSON, mentions_apps JSON, word_count |
bib_endpoints | Metadata extendida para nodes endpoint | method, path UK con method, operation_id, tags JSON, request_schema, response_schema, parameters JSON, deprecated |
bib_agents | Jerarquía agentes (bibliotecario, escribas, libreros, devs, agente-usuario) | agent_id UK, role, model, tier, parent_agent_id, permissions JSON, scope JSON |
bib_index_runs | Auditoría de indexaciones | run_type, status, source, nodes_created, nodes_updated, edges_created, errors JSON, duration_ms |
bib_change_log | Cambios detectados entre indexaciones | node_id FK, change_type, field_changed, old_value, new_value, run_id FK |
bib_federation | Multi-biblioteca (futuro) | library_id UK, base_url, last_sync_at, sync_status |
bib_chunks | Chunks de contenido para semantic search | node_id FK, chunk_index, content, word_count, content_hash, embedding_b64, vectorize_id |
bib_communities | Clusters detectados (Louvain) | community_index UK, label, member_count, cohesion, bridge_nodes JSON, top_nodes JSON |
bib_wiki_pages | Páginas wiki Supercontexto (esta misma página) | slug UK, `type CHECK(entity_page |
bib_wiki_log | Append-only bitácora de operaciones wiki | `operation CHECK(ingest |
bib_wiki_contradictions | Conflictos detectados por Lint | page_a_slug, page_b_slug, claim_a, claim_b, subject, `status CHECK(open |
bib_wiki_utility | Eventos de uso per-page | page_slug, `event CHECK(query_hit |
3.6 Chat del equipo + activity_log + ai_eval_runs
erDiagram
chat_messages {
int id PK
string author "Edu|Dani|Txell"
string body
string created_at
}
chat_typing {
string author PK
string last_typed_at "TTL 5s"
}
activity_log {
int id PK
string timestamp
string actor
string action
string entity_type
string entity_id
string entity_title
string details "JSON"
string source "default mcp"
int duration_ms
}
ai_eval_runs {
int id PK
string created_at
string user_email
string candidates "JSON array"
string cases "JSON array"
string results "JSON matrix"
int total_cells
int duration_ms
string status "completed|partial|failed"
string notes
}
auth_codes {
string nonce PK
string used_at
string created_at
}
4. Persistencia híbrida wiki
Cada página wiki vive en dos sitios a la vez:
src/content/wiki/<slug>.md (commit GitHub, source of truth para Astro build)
↕
bib_wiki_pages D1 (metadata, lint, search, utility tracking)
Flujo de creación
| Vía | Atomicidad |
|---|---|
wiki_create_page MCP | Atómico (commit GitHub + INSERT D1 en la misma transacción) |
Commit manual (.md) | NO atómico — reconcile job en bib_ingest.py propaga metadata a D1 post-merge |
Footguns wiki documentados
| Footgun | Impacto | Sesión |
|---|---|---|
wiki_update_page con patch:{content,...} corrompe el .md (escribe el JSON literal) | s66 corrupción ADR + catálogo | Usar patch como STRING para body |
wiki_create_page no valida schema Astro (objetos en sources requeridos) | s56 — 11 CI rojos consecutivos por CF Pages InvalidContentEntryDataError | Copiar patrón de page hermana antes |
wiki_update_page silent failure en related[] | s32 — returns updated=true sin aplicar | Usar otro hub padre |
| D1↔repo drift | Commits docs: sin pasar por MCP no actualizaban D1 | Sesión 18 fix: branch reconcile en bib_ingest.py |
5. Search semántico · pipeline
.md / código → bib_index_* → bib_nodes + bib_chunks
↓
Embedding (Gemma 4 vía Google AI Studio o Workers AI)
↓
Vectorize (CF) → vectorize_id en bib_chunks
↓
bib_search_semantic tool → top-k chunks por cosine sim
Fallback embebido en D1: embedding_b64 (base64 del vector) usado si Vectorize está caído.
6. Backups y DR
| Mecanismo | Frecuencia | Storage |
|---|---|---|
| Snapshots automáticos CF | Continuos | Cloudflare (sin acceso directo) |
| Wrangler export periódico | Diario (cron STAGE) | /opt/dr-backups/d1-YYYY-MM-DD.sql en STAGE + mirror nginx:8090 |
| GitHub repo | En cada commit | Wiki .md + migrations versionadas (source of truth) |
Restaurar desde cero
# 1. Clonar repo workspace
git clone https://github.com/CreaRackSL/CreaRackSL-workspace.git
# 2. Crear D1 nueva
wrangler d1 create crearacksl-workspace-db
# 3. Importar último dump
wrangler d1 execute crearacksl-workspace-db --remote --file=/opt/dr-backups/d1-LATEST.sql
# 4. Deploy
pnpm build && wrangler pages deploy
7. Operaciones comunes
Query directa contra D1 remoto
# Single query
pnpm wrangler d1 execute crearacksl-workspace-db --remote --command="SELECT COUNT(*) FROM bib_nodes"
# Desde fichero
pnpm wrangler d1 execute crearacksl-workspace-db --remote --file=./query.sql
# Export tabla
pnpm wrangler d1 export crearacksl-workspace-db --table tasks --output tasks.sql
Listar tablas
SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;
Tamaño aproximado por tabla
SELECT name, SUM(pgsize) AS bytes
FROM dbstat
GROUP BY name
ORDER BY bytes DESC;
Migrations aplicadas vs en repo
SELECT * FROM d1_migrations ORDER BY id;
-- comparar con ls migrations/
8. Acceso operativo
| Cómo | Comando |
|---|---|
| Dashboard D1 | https://dash.cloudflare.com → Workers & Pages → D1 → crearacksl-workspace-db |
| Query rápida (workspace UI) | https://workspace.crearack.com/biblioteca (vista del grafo) |
| MCP tools (Claude Code) | mcp__crearack-workspace__bib_*, wiki_*, list_tasks, create_note, etc. |
| Logs Workers (debugging) | pnpm wrangler tail |
| Wrangler local | pnpm wrangler d1 execute crearacksl-workspace-db --local ... |
Véase también
- [[workspace-tech—bd—coloquial]] — Versión accesible para entrada al proyecto
- [[crearack-tech—bd—tecnico]] — BD del SaaS CreaRack-Pro (PostgreSQL)
- [[crearack-tech—general—feature-catalog]] — Sección Biblioteca/Supercontexto desde el lado producto
Referenciado desde
- Base de datos CreaRack Pro · arquitectura técnica
- Base de datos CreaRack Pro · explicación coloquial
- Base de datos workspace · explicación coloquial
- Decisión: Plugin Remark custom para Mermaid en Astro (vs rehype-mermaid)
- Incident: Duplicados en quick_links por tracking roto de migrations D1 (2026-05-14)
- Workspace · Tests de integración con D1 real (pnpm run test:integration)