Panel de administración
La consola de administración (theus.pe/console) es donde el equipo operador gestiona el gateway sin tocar el servidor: proveedores upstream y sus keys, rutas de modelos, el cerebro de TITAN, el catálogo de las organizaciones Atlas, auditoría y métricas. Todo cambio aplica al instante, porque el gateway lee su configuración por request.
Acceso
Los endpoints /api/admin/* exigen una sesión web u OAuth con ámbito de perfil y un rol administrativo efectivo. Los emails de THEUS_ADMIN_EMAILS son propietarios por compatibilidad; los demás operadores entran solo si están registrados en rbac_members. Una API key permanente nunca administra. Cualquier otra credencial recibe 403 forbidden.
Roles por workspace (RBAC). Sobre el acceso base, el gateway resuelve un rol por email y aplica permisos acotados a cada endpoint de gestión. Los emails de THEUS_ADMIN_EMAILS son owner (acceso total). A los demás miembros se les asigna un rol con el alcance justo:
- owner — todo, incluida la gestión de miembros.
- admin — operación completa (proveedores, rutas, TITAN, guardián, mantenimiento, enforcement), sin gestión de miembros.
- billing — suscripciones, packs y saldo; nada de infraestructura.
- auditor — solo lectura de auditoría, métricas y resumen.
- support — ver usuarios y acreditar saldo, nada más.
Así, por ejemplo, quien lleva facturación no puede tocar los proveedores upstream, y un auditor externo solo lee. Los miembros se gestionan en /api/admin/members; el sistema impide dejar el workspace sin ningún owner (anti-lockout).
Planes y créditos de clientes
La ficha del cliente permite cambiar su plan efectivo y agregar créditos sin tocar manualmente el store. Ambas operaciones requieren confirmación, motivo cerrado, nota interna opcional y una clave idempotente; el gateway rechaza con 409 reutilizar esa clave para una intención distinta.
POST /api/admin/subscriptions/plancrea un ajuste administrativo separado de la suscripción Stripe. El plan cobrado y sus identificadores nunca se falsean. Enviarplan: "inherit"retira el ajuste y vuelve al plan real de facturación.POST /api/admin/wallet/creditagrega créditos enteros con categorías como gracia, donación, recarga, compensación, promoción, reembolso, SLA, soporte o migración. Motivo, nota y actor quedan en el ledger y en la auditoría.- Si una cuenta pierde un plan con Enterprise, el mismo cambio revoca pases, correlación y credenciales de consumo. Si vuelve a Omnipotente, la organización se reactiva sin duplicarse en el próximo acceso.
POST /api/admin/atlas/overrideconcede una cortesía del plan Atlas: el espejo del anterior sobre el producto Atlas. Se guarda aparte de la suscripción cobrada y de sus identificadores, así que no falsea lo que el cliente paga; compite por rango con la suscripción Atlas directa y con el plan Atlas que incluye su plan Theus, y gana el escalón mayor — una cortesía nunca degrada una suscripción pagada. Toda cortesía caduca:dias(oexpires_at) es obligatorio al conceder y no puede pasar de 366 días; vencida, deja de existir sola. Enviarplan: "inherit"la retira antes de tiempo. Exige motivo cerrado eidempotency_key, igual que el resto.
Catálogo de las organizaciones Atlas
Cada organización de Atlas by Theus se da de alta en el motor con una credencial propia y el catálogo de modelos del release: sus ids, su ventana de contexto, su techo de salida y si el modelo admite modo pensamiento. Ese alta lleva una huella del catálogo con el que se hizo; cuando un despliegue cambia el catálogo, las organizaciones ya aprovisionadas quedan con la huella vieja.
Hasta ahora esa puesta al día era perezosa: cada organización se re-aprovisionaba en la siguiente visita de su cliente, y ese trabajo —rotar la credencial y volver a registrar los ~40 modelos río arriba— corría dentro de la propia petición de su navegador. Un cambio de catálogo llegaba a los clientes nuevos y a ninguno de los que ya pagaban, en silencio.
POST /api/admin/enterprise/reconciliar → 202 { ok, estado, en_proceso[], al_dia[], omitidas[] }
GET /api/admin/enterprise/reconciliar → { estado, pendientes, resultados[] }
- Se dispara una vez tras el despliegue y devuelve al instante (
202): recorre las organizaciones desfasadas en segundo plano, en un hilo propio, sin ocupar el pool que despacha avisos. - Una sola pasada viva: un segundo disparo con otra en curso recibe
409 reconciliacion_en_curso. Dos pasadas a la vez no rompen nada, pero duplicarían rotaciones de credencial sin ganar nada. - Cortacircuitos: tras 3 fallos seguidos la pasada se aborta en vez de recorrer el resto del padrón. Un re-alta fallido deja las filas de modelos apuntando a una credencial retirada; con el motor caído, seguir sería hacérselo a todas las organizaciones de una sentada.
- El
GETes solo lectura y devuelve el avance de la última pasada, con una fila por organización y su veredicto:re_aprovisionada,al_dia,error,revocada,sin_planono_intentada(las que quedaron sin tocar cuando el cortacircuitos abortó la pasada; el estado global de esa pasada esabortado). Es un snapshot en memoria: un reinicio del servicio lo borra y la siguiente pasada lo recalcula entero desde el store. - Permisos:
POSTexigeproviders:write;GET,providers:read. El disparo queda en la auditoría comoenterprise.gateway.reconcile.
Proveedores upstream
Un proveedor es un upstream de inferencia con base_url, api_key, formato de API, prioridad y estado. La regla central del diseño:
0600); en cualquier respuesta del API viaja enmascarada (p. ej. sk-p…abc4) y jamás se loguea en claro.| Campo | Qué es |
|---|---|
name | Nombre visible del proveedor (obligatorio). |
base_url | URL http(s) del API upstream, p. ej. https://api.proveedor.com/v1. |
api_key | Credencial upstream. Solo en el store; enmascarada en toda respuesta. |
api_format | openai (default, compatible OpenAI) o anthropic (API nativa /v1/messages). |
enabled | Un proveedor deshabilitado no sirve tráfico. |
priority | Entero (menor = primero). El primer proveedor habilitado y completo es el transporte por defecto cuando no hay entorno THEUS_UPSTREAM_*. |
notes | Notas operativas libres. |
Formato anthropic con caching nativo
Cuando un proveedor declara api_format: "anthropic", el gateway traduce cada request canónico al API nativo /v1/messages (headers x-api-key + anthropic-version) y devuelve la respuesta —y el stream— en formato chat.completion. La pieza crítica es el prompt caching explícito: el adaptador marca los breakpoints de cache_control (última tool, último bloque system y último bloque del último mensaje) para que los ~20K tokens fijos de system+tools del CLI se cobren a precio de cache-read en cada turno.
Prueba de conexión real
POST /api/admin/model-providers/{id}/test prueba la key guardada contra el proveedor de verdad: primero GET /models (o GET /v1/models nativo) y, si el upstream no lo expone o pide otra auth, un chat mínimo de 1 token con un modelo de sondeo (el que indiques en el body, el de la primera ruta que apunte al proveedor, o el default). El resultado (ok, HTTP, latencia, detalle) queda guardado en last_test y en la auditoría.
Rutas de modelos
Las rutas mapean cada id del catálogo (o un alias propio) a {provider_id, upstream_model}. Es la versión administrable de THEUS_MODEL_MAP, que queda como fallback por entorno. PUT /api/admin/model-routes reemplaza el mapa completo y valida cada entrada: el modelo debe ser del catálogo, del mapa env o un alias válido, y el provider_id debe existir.
PUT /api/admin/model-routes
{
"routes": {
"glm-4-6": {"provider_id": "prov_…", "upstream_model": "glm-4.6"},
"theus-embed": {"provider_id": "prov_…", "upstream_model": "text-embedding-…"}
}
}
- Un modelo con ruta usa el
base_url+ key de su proveedor: multi-proveedor real, modelo por modelo. - Al borrar un proveedor, sus rutas se eliminan y esos modelos vuelven al fallback env; si el cerebro TITAN apuntaba a él, se desacopla. Todo queda auditado.
- TITAN solo reparte subtareas a modelos con ruta a proveedor operativo (ver TITAN).
Cerebro TITAN
GET/PATCH /api/admin/titan-config gestiona el cerebro orquestador: enabled, provider_id (un proveedor del panel), model y max_subtasks (1–6). El GET devuelve tres vistas: config (lo guardado en el panel), effective (lo que realmente rige, con su source: store, env o default) y env (los valores TITAN_* del entorno). La prioridad efectiva es panel > entorno > transporte por defecto del gateway.
Auditoría
GET /api/admin/audit devuelve la bitácora de acciones admin (últimos 200 eventos): quién (actor), qué (action: provider.create, provider.update, provider.delete, provider.test, routes.replace, titan.update, la gestión de miembros…), sobre qué (target) y cuándo, con el detalle del cambio. El detalle nunca contiene secretos completos: las keys van enmascaradas también aquí.
A prueba de manipulación (hash-chain). Cada entrada queda encadenada al hash de la anterior: alterar o borrar un evento pasado rompe la cadena, y eso se detecta. GET /api/admin/audit/verify recorre la bitácora, recomputa cada hash y confirma el encadenado, devolviendo {ok, verified_count, break_at_seq, …}. Es la evidencia de integridad que pide una due-diligence o una auditoría de seguridad: no solo hay registro, sino que se puede probar que no se tocó.
Métricas y vistas de solo lectura
| Endpoint | Qué devuelve |
|---|---|
GET /api/admin/overview | Panel general: usuarios, API keys (activas/revocadas/expiradas), suscripciones activas por plan, tokens del periodo y totales, proveedores y rutas, estado efectivo de TITAN y del gateway. |
GET /api/admin/users | Usuarios con plan efectivo, número de keys y uso del periodo. |
GET /api/admin/models | El catálogo completo en formato de API. |
GET /api/admin/providers | Vista heredada: el upstream por entorno + los proveedores gestionados, con estado. |
Referencia de endpoints de gestión
| Método y ruta | Acción |
|---|---|
POST /api/admin/subscriptions/plan | Ajusta o retira el plan administrativo de un cliente, sin sobrescribir Stripe. |
POST /api/admin/wallet/credit | Abona créditos con motivo, nota, actor e idempotencia auditados. |
POST /api/admin/atlas/override | Concede o retira la cortesía del plan Atlas. Caduca siempre (dias o expires_at, máximo 366). |
POST /api/admin/enterprise/reconciliar | Empuja el catálogo del release a las organizaciones Atlas desfasadas, en segundo plano (202; 409 si ya hay una pasada en curso). |
GET /api/admin/enterprise/reconciliar | Avance y veredicto por organización de la última pasada. |
GET /api/admin/model-providers | Lista proveedores (keys enmascaradas) + estado del fallback env. |
POST /api/admin/model-providers | Crea un proveedor (name, base_url, api_key, api_format, enabled, priority, notes). |
PATCH /api/admin/model-providers/{id} | Actualiza cualquiera de esos campos. |
DELETE /api/admin/model-providers/{id} | Elimina el proveedor, sus rutas dependientes y lo desacopla de TITAN. |
POST /api/admin/model-providers/{id}/test | Prueba de conexión real con la key guardada. |
GET /api/admin/model-routes | Rutas actuales + catálogo + mapa env de referencia. |
PUT /api/admin/model-routes | Reemplaza el mapa completo modelo → proveedor. |
GET /api/admin/titan-config | Config del cerebro: guardada, efectiva y del entorno. |
PATCH /api/admin/titan-config | Actualiza enabled, provider_id, model, max_subtasks. |
GET /api/admin/audit | Bitácora de acciones admin (últimos 200 eventos). |
Dónde vive el estado
El gateway persiste todo (usuarios, keys, billing, proveedores, rutas, config de TITAN, auditoría) en un store SQLite en modo WAL (oauth-store.sqlite3, permisos 0600): escrituras atómicas por transacción y lecturas con snapshot. Periódicamente exporta un oauth-store.json de paridad (mismo formato histórico, con backups rotados) para respaldo y restauración. La migración desde el store JSON histórico es automática y única al arrancar; para volver al backend JSON: THEUS_STORE_BACKEND=json. Mantenimiento manual: python theus_oauth_server.py --migrate | --export-json | --backend-info.
.bak y reinicia el servicio. Nunca se pierde estado en silencio.