Files
fn_registry/dev/issues/completed/0008-sqlite-api-web.md
T
2026-04-13 01:24:24 +02:00

7.0 KiB

0008 — SQLite API Web

Metadata

Campo Valor
ID 0008
Estado completado
Prioridad alta
Tipo feature

Dependencias

Ninguna.


Objetivo

App que expone registry.db y los operations.db de cada app como API REST HTTP, permitiendo que herramientas externas (dashboards, scripts, agentes, frontends) consulten las bases de datos del registry sin necesidad de acceso directo al filesystem ni SQLite CLI.

Contexto

  • Actualmente para consultar registry.db hay que estar en la misma máquina y usar sqlite3 directamente o funciones Go que abren el archivo.
  • Las apps existentes (metabase_registry, registry_dashboard) acceden a SQLite localmente. Cualquier herramienta nueva que necesite datos del registry tiene que reimplementar la conexión.
  • Con una API web, cualquier cliente HTTP (curl, fetch, Python requests, frontends React) puede consultar el registry de forma uniforme.
  • Metabase ya resuelve visualización, pero no da acceso programático limpio a los datos para agentes y scripts remotos.

Arquitectura

apps/sqlite_api/
├── main.go          — NEW: Entry point, configura rutas y arranca servidor
├── handlers.go      — NEW: Handlers HTTP (query, tables, schema)
├── config.go        — NEW: Configuración (puerto, DBs permitidas, read-only)
├── app.md           — NEW: Metadata de la app (tag: service)
└── operations.db    — Runtime: operaciones propias

Patrón pure core / impure shell

  • Funciones del registry usadas: http_get_json_go_infra, http_post_json_go_infra (para tests/clientes), cache_to_sqlite_go_infra (opcional para cache de queries)
  • Core puro: validación de queries (solo SELECT/PRAGMA permitidos), parsing de parámetros, formateo de resultados JSON
  • Shell impuro: servidor HTTP, apertura de SQLite, ejecución de queries

Diseño de API

Endpoints

GET  /api/databases                        — Lista de DBs disponibles
GET  /api/databases/:db/tables             — Lista tablas de una DB
GET  /api/databases/:db/schema             — Schema completo (.schema)
POST /api/databases/:db/query              — Ejecuta query SQL (solo SELECT)
GET  /api/databases/:db/fts?q=texto&table=functions  — Búsqueda FTS5 directa
GET  /health                               — Health check

Bases de datos expuestas

Alias Path real Descripción
registry registry.db (raíz) Funciones, tipos, proposals
ops:{app} apps/{app}/operations.db Entities, relations, executions de cada app

Seguridad

  • Read-only obligatorio: Solo queries SELECT y PRAGMA. Cualquier INSERT/UPDATE/DELETE/DROP se rechaza antes de ejecutar.
  • Bind por defecto a localhost (127.0.0.1:8484). Flag --bind para cambiar.
  • Sin autenticación en v1 (solo acceso local). Documentar cómo poner detrás de reverse proxy si se necesita auth.
  • Query timeout: máximo 5 segundos por query para evitar bloqueos.
  • Apertura con ?mode=ro en el connection string de SQLite para doble protección.

Formato de respuesta

// POST /api/databases/registry/query
// Body: {"sql": "SELECT id, name, purity FROM functions WHERE domain = 'core' LIMIT 5"}
{
  "columns": ["id", "name", "purity"],
  "rows": [
    ["filter_slice_go_core", "filter_slice", "pure"],
    ["map_slice_go_core", "map_slice", "pure"]
  ],
  "count": 2,
  "duration_ms": 3
}

Tareas

Fase 1: Servidor base

  • 1.1 Crear apps/sqlite_api/ con main.go, go.mod (o usar módulo raíz)
  • 1.2 Handler /health y /api/databases (lista estática de DBs detectadas)
  • 1.3 Handler POST /api/databases/:db/query con validación read-only
  • 1.4 Abrir DBs con ?mode=ro y -tags fts5
  • 1.5 app.md con tag service, documentar puerto y health check

Fase 2: Endpoints de exploración

  • 2.1 Handler /api/databases/:db/tables (lista tablas vía sqlite_master)
  • 2.2 Handler /api/databases/:db/schema (output de .schema)
  • 2.3 Handler /api/databases/:db/fts para búsqueda FTS5 sin escribir SQL

Fase 3: Operations discovery

  • 3.1 Auto-detectar apps/*/operations.db al arrancar
  • 3.2 Exponer cada operations.db como ops:{app_name}
  • 3.3 Endpoint GET /api/databases incluye las operations detectadas

Fase 4: Cleanup y docs

  • Crear app.md completo
  • Ejecutar go vet y go test
  • Actualizar issue en dev/issues/README.md

Ejemplo de uso

# Arrancar el servicio
cd apps/sqlite_api && go run . --port 8484

# Health check
curl http://localhost:8484/health

# Listar databases disponibles
curl http://localhost:8484/api/databases

# Query al registry
curl -X POST http://localhost:8484/api/databases/registry/query \
  -H "Content-Type: application/json" \
  -d '{"sql": "SELECT id, purity, description FROM functions WHERE domain = '\''core'\'' LIMIT 5"}'

# Búsqueda FTS5
curl "http://localhost:8484/api/databases/registry/fts?q=slice&table=functions"

# Schema
curl http://localhost:8484/api/databases/registry/schema

# Query a operations de una app
curl -X POST http://localhost:8484/api/databases/ops:pipeline_launcher/query \
  -H "Content-Type: application/json" \
  -d '{"sql": "SELECT * FROM executions ORDER BY started_at DESC LIMIT 10"}'
# Desde Python
import requests

r = requests.post("http://localhost:8484/api/databases/registry/query", json={
    "sql": "SELECT id, name FROM functions WHERE purity = 'pure' AND domain = 'core'"
})
data = r.json()
for row in data["rows"]:
    print(row[0], row[1])

Decisiones de diseño

  • Go con net/http estándar: sin framework externo, coherente con el resto del registry. Router simple con http.ServeMux.
  • Puerto 8484: no colisiona con Metabase (3000), Jupyter (8888), ni otros servicios comunes.
  • Read-only estricto: la API nunca modifica datos. Para escribir se usan los mecanismos existentes (fn ops, fn index).
  • Sin ORM: queries se pasan tal cual a SQLite. El valor es el acceso HTTP, no una capa de abstracción SQL.
  • Auto-discovery de operations.db: escanea apps/*/operations.db al inicio para no tener que configurar cada app manualmente.

Riesgos

  • SQL injection vía queries arbitrarias: Mitigado con apertura read-only (?mode=ro) + validación de que el statement empieza con SELECT o PRAGMA.
  • Queries pesadas bloquean el servidor: Mitigado con timeout de 5s por query y context cancelable.
  • Archivos SQLite bloqueados por escritores concurrentes: Mitigado con journal_mode=wal y apertura read-only que no bloquea escritores.

Criterios de aceptación

  • curl localhost:8484/health retorna 200
  • Queries SELECT funcionan contra registry.db
  • Queries INSERT/UPDATE/DELETE son rechazadas con 400
  • Operations.db de apps existentes son accesibles como ops:{nombre}
  • FTS5 funciona a través de la API
  • Tag service en app.md
  • El servidor arranca con go run . sin configuración adicional