Files
fn_registry/docs/capabilities/backends.md
T
egutierrez 53a3cdbda9 chore: auto-commit (8 archivos)
- .claude/rules/registry_calls.md
- apps/dag_engine/README.md
- apps/dag_engine/app.md
- docs/capabilities/INDEX.md
- docs/capabilities/systemd.md
- docs/execution_standard.md
- dev/proposals_e2e_checks_0121/
- docs/capabilities/backends.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 00:31:30 +02:00

11 KiB

Capability: backends

Catalogo de stacks backend usados en apps/. Para que un agente que va a construir un backend nuevo sepa que stack elegir, que funciones del registry componer y que esqueleto copiar sin reinventar nada.

No es un grupo tag: del registry — es una guia transversal (los stacks atraviesan dominios). Complementa cpp_apps.md (frontend C++) y deploy.md (entrega).

Stacks soportados

Stack Lang Cuando elegir Apps de referencia
Go net/http stdlib + SQLite go Default. API HTTP local o detras de Traefik. Persistencia SQLite con migraciones embed.FS. sqlite_api, services_api, registry_api, kanban, deploy_server, dag_engine, agent_runner_api, call_monitor
Go MCP stdio/http go Tool server para Claude/agentes. Lectura/escritura registry. registry_mcp
Go bubbletea TUI go CLI interactiva sin HTTP. Pipeline launcher, docker manager. pipeline_launcher, docker_tui, dev_console
Go mautrix bot go Bot Matrix con E2EE, LLM tools, comandos. agents_and_robots
Python httpx + YAML py Cliente declarativo de API REST externa (Metabase, Gitea, etc.). Pull/push contra disco. auto_metabase, metabase_registry
Bash docker-compose bash Stack multi-container (Synapse + Element + LiveKit, PostGIS + Valhalla). element_matrix_chat, footprint_geo_stack
C++ ImGui frontend cpp NO es backend — cliente HTTP/WS de los Go services. Ver cpp_apps.md. services_monitor, registry_dashboard, ...

Decision tree

¿Necesitas HTTP API?
├─ si  → ¿Es para Claude/agentes?
│       ├─ si  → MCP server (Go) → copiar `registry_mcp`
│       └─ no  → Go net/http stdlib + SQLite + embed.FS migrations → copiar `services_api`
│
└─ no  → ¿Interactivo terminal?
        ├─ si  → Go bubbletea → copiar `pipeline_launcher`
        └─ no  → ¿Cliente de API externa?
                ├─ si  → Python httpx → copiar `auto_metabase`
                └─ no  → ¿Stack de containers?
                        ├─ si  → Bash + docker-compose.yml → copiar `element_matrix_chat`
                        └─ no  → reevalua, probablemente sea funcion del registry no app

Esqueleto canonico: Go net/http stdlib + SQLite (el default)

Layout

apps/<name>/
  app.md                       # frontmatter + service: block (issue 0105)
  main.go                      # flags + listener + signal handling
  server.go                    # http.ServeMux + routes + middleware chain
  db.go                        # sql.Open con WAL + FK + apply migrations
  handlers_*.go                # 1 archivo por recurso
  migrations/
    001_init.sql               # CREATE TABLE IF NOT EXISTS (idempotente)
    002_*.sql                  # aditivo. NUNCA modificar 001 ya commiteado
  operations.db                # SQLite local. Gitignored.

Frontmatter app.md

---
name: my_service
lang: go
domain: infra            # ver list_domains
version: 0.1.0
description: "1 linea: que hace + por que existe."
tags: [service, api, http, sqlite]
uses_functions:
  - sqlite_apply_versioned_migrations_go_infra
  - http_serve_go_infra
  - http_json_response_go_infra
  - http_parse_body_go_infra
  - http_router_go_infra
  - http_cors_middleware_go_infra
  - http_logger_middleware_go_infra
  - logger_go_infra
uses_types: []
framework: "net/http"
entry_point: "main.go"
dir_path: "apps/my_service"
service:                 # OBLIGATORIO si tag service. issue 0105
  port: 8500
  health_endpoint: /api/health
  health_timeout_s: 3
  systemd_unit: my_service.service
  systemd_scope: user
  restart_policy: always       # NUNCA on-failure (ver gotcha cpp_apps.md)
  runtime: systemd-user
  pc_targets: [home-wsl]
  is_local_only: false
---

main.go (minimo viable)

package main

import (
    "context"
    "flag"
    "log"
    "os/signal"
    "syscall"
)

func main() {
    bind := flag.String("bind", "127.0.0.1:8500", "addr to listen on")
    dbPath := flag.String("db", "operations.db", "sqlite path")
    flag.Parse()

    ctx, cancel := signal.NotifyContext(context.Background(),
        syscall.SIGINT, syscall.SIGTERM)
    defer cancel()

    db, err := openDB(*dbPath)              // db.go — usa sqlite_apply_versioned_migrations
    if err != nil { log.Fatal(err) }
    defer db.Close()

    srv := newServer(db)                    // server.go — http.ServeMux + rutas
    log.Printf("listening on %s", *bind)
    if err := serve(ctx, *bind, srv); err != nil { log.Fatal(err) }
}

Build + service unit

CGO_ENABLED=1 go build -tags fts5 -o my_service .
systemctl --user enable --now my_service.service    # tras generar unit

Esqueleto canonico: MCP server Go

Copia registry_mcp:

  • main.go parsea flags (--stdio o --http).
  • Cada tool = funcion Go con schema JSON y handler.
  • Schema generation con jsonschema reflect.
  • Read-only por default; --enable-run / --enable-write gating.

Funciones del registry relevantes: cualquier de mcp__registry__fn_* para entender shape. Patron de gating en apps/registry_mcp/main.go.

Esqueleto canonico: Python httpx cliente declarativo

Copia auto_metabase:

  • python/.venv/bin/python3 como interprete.
  • Importa wrappers del registry (from infra import metabase_auth, metabase_get_dashboard, ...).
  • YAML manifest define estado deseado.
  • Pull = volcar a YAML. Push = enviar a API.
  • NUNCA requests.post(...) directo — siempre via wrapper que ya hace auth + retry + telemetria.
import sys, os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "python", "functions"))
from infra import metabase_auth, metabase_list_dashboards
session = metabase_auth(host, user, pwd)
for d in metabase_list_dashboards(session):
    ...

Esqueleto canonico: bubbletea TUI

Copia pipeline_launcher:

  • tea.Model con Init/Update/View.
  • Sin HTTP. Estado local + acciones sobre disco/registry.
  • Tag launcher si debe aparecer en el Pipeline Launcher (function_tags.md).

Esqueleto canonico: Bash docker-compose stack

Copia element_matrix_chat:

  • docker-compose.yml + .env + scripts up.sh/down.sh.
  • Sin codigo Go/Py — solo composicion de imagenes.
  • Tag service + runtime: docker-compose en service:.
  • Deploy via docker_compose_remote_deploy_bash_infra (ver capability deploy).

Funciones del registry — paleta backend Go

ID Para
sqlite_apply_versioned_migrations_go_infra Aplicar migrations/*.sql con embed.FS al arrancar
sqlite_open_*_go_infra Abrir SQLite con WAL + foreign keys + ping
http_serve_go_infra Listener con graceful shutdown via ctx
http_router_go_infra *http.ServeMux desde []Route declarativo
http_json_response_go_infra Escribir JSON + status code
http_error_response_go_infra Errores con shape consistente
http_parse_body_go_infra Decode JSON con maxBytes (anti-DoS)
http_cors_middleware_go_infra CORS para frontend ImGui/web
http_logger_middleware_go_infra Log estructurado por request
http_middleware_chain_go_infra Componer middlewares
http_session_cookie_middleware_go_infra Sesiones cookie con TTL
http_session_cookie_set_go_infra / clear / extract CRUD cookie sesion
jwt_middleware_go_infra Auth JWT (alternativa a cookies)
crud_generate_handlers_go_infra 5 handlers REST a partir de CRUDResource
crud_register_routes_go_infra Registrar GET/POST/PUT/DELETE /resource[/id] en mux
file_serve_go_infra Static files con Cache-Control: max-age
health_check_http_go_infra Polling de URL hasta 2xx (smoke en tests)
logger_go_infra Logger estructurado (consumido por log_window apps C++)
notify_telegram_go_infra Alertas a chat (opcional)
ssh_exec_go_infra Ejecutar comandos en host remoto (services cross-PC)

Buscar mas: mcp__registry__fn_search query="http" lang="go" tag="service".

Patron CRUD declarativo

Si el backend expone una entidad CRUD plana (cards, jobs, deploys), evita escribir 5 handlers a mano:

res := infra.CRUDResource{
    Table:    "cards",
    PKColumn: "id",
    Columns:  []string{"id","title","status","created_at"},
}
mux := http.NewServeMux()
infra.CRUDRegisterRoutes(mux, "/api/cards", res, db)
// GET /api/cards          → list
// GET /api/cards/{id}     → get
// POST /api/cards         → create
// PUT /api/cards/{id}     → update
// DELETE /api/cards/{id}  → delete

Service: block (issue 0105) — checklist obligatorio

Toda app con tag: service declara:

  • port (null si stdio).
  • health_endpoint (ruta GET 2xx → sano).
  • health_timeout_s (default 3).
  • systemd_unit si runtime empieza con systemd-.
  • systemd_scope: user | system.
  • restart_policy: always (no on-failure — ver gotcha en function_tags.md).
  • runtime: systemd-user | systemd-system | docker-compose | stdio | manual.
  • pc_targets[]: pc_ids de pc_locations.

Auditar: fn doctor services-spec.

Gotchas comunes

Gotcha Causa Solucion
Service muere en SIGTERM y systemd no reinicia Restart=on-failure en unit Cambiar a Restart=always. sqlite_api cayo 20h asi (2026-05-17)
database is locked al escribir desde 2 procesos SQLite sin WAL Abrir con ?_journal_mode=WAL&_foreign_keys=on (usar sqlite_open_*)
Frontend ImGui no recibe CORS preflight Falta http_cors_middleware Anadir middleware antes del router
Body POST llega vacio json.NewDecoder(r.Body) sin maxBytes — falla silenciosa Usar http_parse_body_go_infra con maxBytes=10MB
Migracion 002 borra datos en otros PCs al hacer fn sync Migracion destructiva Solo aditivo. Ver db_migrations.md
MCP tool no aparece en Claude tras anadirla Schema no regenerado Rebuild + claude mcp remove/add o reset cache

Fronteras (que NO cubre esta capability)

  • Apps C++ frontendcpp_apps.md + capability cpp-dashboard-viz.
  • Deploy del backend a VPS → capability deploy (Docker+Traefik, systemd, rsync).
  • Frontend web Vite/React/Mantine consumido por el backend → capability mantine.
  • Migraciones de schema → regla db_migrations.md.
  • Telemetria de calls del agente al registry → registry_calls.md + call_monitor app.

Cuando promover a pipeline / extraer al registry

Si tras construir el N-esimo Go service detectas patron repetido (>2 apps):

  1. Buscar funcion existente en registry (mcp__registry__fn_search).
  2. Si no existe → spawn fn-constructor con tag de grupo (http, service, etc.).
  3. Migrar las apps existentes para consumirla.
  4. Capability page se actualiza sola via fn doctor capabilities.