Gateway y overlook

Una sola conexión local que guarda tus llaves de proveedor, y un vínculo opcional al overlook SaaS que nunca ve tu código.

Qué es#

El gateway es un proxy de loopback opcional, por defecto 127.0.0.1:8788, para asientos que hablan con modelos con API keys. No reemplaza los logins de plan de Cursor / Claude / Codex — esos asientos conservan su cuenta; Orchemax los gobierna (MCP, bus, locks) sin enrutar su tráfico de modelos.

  agente con API key ---> orch gateway ---> proveedor A  (llave 1, llave 2, llave 3)
                               |        \--> proveedor B  (llave 1, llave 2)
                               |
                               +-- crush (encoge el payload)
                               +-- rota ante rate limits y enfría la llave
                               +-- caché opcional de respuestas por coincidencia exacta

  Cursor / Claude (plan) / Codex ---> API de modelos del vendor   (sin gateway)

Tus llaves van a una bóveda en el machine home — keys.env, compartida por todos los workspaces — y varias por proveedor cuando las tienes:

orch gateway keys add <provider> [key]     # sondea la conectividad primero
orch gateway keys list [provider]          # solo pistas y tier, nunca el secreto
orch gateway keys remove <provider> <hint>
orch gateway status                        # vivo / enfriamiento, posición del anillo, rotaciones

orch gateway status lee directo del router y es libre de contenido: sin llaves, sin prompts. El agente nunca tiene una llave ni ve un 429: el gateway rota por el anillo del proveedor y comprime cada llamada.

Dos lugares donde puede vivir una llave de proveedor#

El gateway no pregunta en qué escalón del plan estás: le pide una llave a un resolvedor de credenciales y recibe una. Cuál backend responde se decide una sola vez, según el plan:

Plan Backend Dónde está la llave
Free · Starter · Professional local Tu máquina: keys.env en machine-home, o un api_key en tu configuración
Team · Enterprise server Tu cuenta de Orchemax. No se escribe nada en la máquina

Una persona con su propia llave no tiene un CTO a quien confiársela, y no le vamos a pedir que la suba. En una empresa es al revés: una llave en cada laptop es una llave que se va con quien se va.

En Team y Enterprise el CTO deja el anillo una sola vez:

orch server keys add <proveedor> <llave> [más llaves…]   # dejar o rotar
orch server keys list                                    # solo pistas, nunca el secreto

Cada máquina arrienda una copia por quince minutos: suficiente para sobrevivir cualquier llamada al modelo, y poco para que una credencial en un proceso que nadie selló deje de funcionar por sí sola. El gateway la renueva un minuto antes de que expire, así que refrescar es un paso normal y nunca una petición fallida. El arriendo se guarda en memoria: no se escribe en keys.env, no se escribe en orch.db, y se enmascara en los logs como cualquier otra credencial. Cada arriendo queda registrado contra el dispositivo que lo pidió, y por eso el rastro de la cuenta puede decir qué máquina gastó qué.

El techo, dicho sin adornos: la llave arrendada sí llega a la memoria de la máquina, así que esto compra rotación central y revocación inmediata —desvincula el dispositivo y su siguiente arriendo falla— y no una llave que la máquina nunca ve. Cuando la máquina se va, en disco no queda nada.

Dos claves de política llevan esto en tus entitlements, y una fila de tenant puede sobrescribir cualquiera de las dos:

Clave de feature Significado
ai.byok Las llaves de machine-home cuentan en este plan
ai.keys.server El servidor emite credenciales de corta vida

Un taller sin servidor sigue funcionando: por debajo de Team nada en este camino toca la red, y a una máquina Team que no alcanza el servidor se le dice, en vez de caer en silencio a cualquier llave que haya quedado en disco.

Apunta un agente con API key con orch gateway connect (paste OpenAI-compat) o connect anthropic; opcionalmente connect opencode --allow-user-scope escribe el JSON de OpenCode. No conectes asientos de plan (Cursor / Claude con suscripción / Codex).

La caché de respuestas está apagada por defecto. gateway.cache.ttl_sec es decisión del dueño — 0 la desactiva —, el streaming nunca se cachea, las entradas se llavean por credencial, y una respuesta cacheada lleva X-Orchemax-Cache: hit sin contar como intento contra el proveedor.

Los asientos facturados por plan conservan su login#

Los términos de Anthropic prohíben que un tercero enrute pedidos a través de credenciales Free, Pro o Max, o que guarde esos tokens, así que el gateway responde 403 con un rechazo de una línea a cualquier pedido que lleve un bearer OAuth de Anthropic o la cabecera beta de OAuth. Un asiento de Claude con sesión de su plan no enruta tráfico de modelo por Orchemax; una llave de API de Anthropic es la única credencial que la vía de passthrough transporta.

Esto se expresa por preset como agents.auth.<preset>:

Valor Significado
account El agente conserva el login de su plan; Orchemax no le inyecta credenciales de proveedor
gateway El agente se autentica con la llave virtual de Orchemax y todo el tráfico de modelo pasa por el gateway

Un CLI que ignora una base URL propia mientras tiene sesión abierta usa account por defecto: forzarle credenciales reemplazaría el login de la suscripción y desactivaría sus conectores sin comprar nada.

Cada worker recibe su propia llave#

Un worker despachado nunca recibe la llave del gateway del workspace. Al despacharlo, Orchemax acuña una llave con alcance de esa corrida — orch_wk_…, atada a la sesión y al rol, con vencimiento al timeout del despacho más diez minutos — y esa es la única credencial de modelo que hay en su entorno. Las llaves de proveedor que el propio Chair tenga se quitan de lo que el worker hereda.

Tres cosas terminan con la llave, y gana la primera que ocurra:

  • El sellado. El agente se detiene y en el mismo paso se revoca la llave.
  • El vencimiento. Una llave que nadie revocó deja de autenticar sola.
  • El reaper. Un worker matado a media corrida nunca llega al sellado; la pasada de reap revoca las llaves de toda sesión abandonada.
orch gateway key workers     # llaves de worker vivas: id, sesión, rol, vencimiento — nunca el secreto

Como la llave nombra su sesión, el gateway sabe de quién es la llamada que atiende: el consumo aterriza en ese despacho aunque ningún CLI de worker mande una cabecera de sesión propia. Solo se guarda el hash, y orch_wk_ se enmascara en los logs como cualquier otra credencial.

Enviar el consumo a tu propio stack de observabilidad#

orch usage export manda el ledger de consumo, opt-in y solo adonde tú apuntes:

orch usage export                                    # JSONL con llaves gen_ai.* en stdout
orch usage export --otlp http://127.0.0.1:4318       # se le agrega /v1/traces
orch usage export --otlp $URL --since 24h --header "authorization=Bearer $TOKEN"

Con --otlp cada llamada al modelo viaja como un span CLIENT de OpenTelemetry en OTLP/HTTP JSON, con gen_ai.system, gen_ai.request.model, gen_ai.operation.name y gen_ai.usage.input_tokens/output_tokens, más orch.session y orch.agent. Las llamadas de una misma sesión comparten traza. Los spans viajan por lotes, cada POST con diez segundos de plazo, y un collector que rechaza hace fallar el comando en lugar de quedar tragado.

Un intento que nunca llegó al proveedor no lleva ninguna llave gen_ai.usage.*: orch.terminal dice por qué, en vez de un cero disfrazado de medición. Nada más cruza el cable: el ledger de intentos detrás de esto nunca guardó un prompt ni una respuesta, así que ahí no hay nada que redactar.

El overlook#

Vincular un workspace al overlook de Orchemax es opcional y reversible:

orch server login       # inicia sesión y vincula este dispositivo
orch server link --token <token>
orch server status
orch server entitlements
orch server watch       # mantiene el canal vivo abierto y drena el correo al llegar
orch server unlink

orch server watch se suscribe a GET /v1/orch/stream, un canal de server-sent-events detrás del mismo token de dispositivo que el resto de /v1/orch. El evento lleva un solo campo, el id pendiente más alto del outbox: es un timbre, no una entrega. El correo se drena por el sync que ya existe, así que un taller tres eventos atrasado se pone al día con un solo timbre. Un canal ocioso escribe una línea de comentario cada 25 segundos para sobrevivir a los proxies que cortan un stream callado.

El poll queda como respaldo. Un servidor sin la ruta, una conexión caída o una reconexión todavía en espera cuestan latencia, nunca entrega: --poll (60s por defecto) drena por su cuenta de todos modos.

Lo que cruza el cable son contadores y metadatos: entitlements, pulso de trabajo libre de contenido, eventos de asiento. El código fuente no. El ledger de consumo que guarda el cliente nunca almacena prompts, que es lo que vuelve segura esa lectura. Un taller sin vincular sigue funcionando, sin conexión.

Los agentes consultan el plan con entitlements_status primero. Las denegaciones van en lenguaje claro (el agente debe citarlas tal cual):

  • Fuera de horario → Outside the Team work schedule — agents cannot dispatch right now… (ver Horarios de trabajo del Team)
  • Capacidad de un plan más alto → X needs the Team plan (you have free). Upgrade: …
  • Cupo de workers lleno → free plan allows 1 concurrent worker; 1 already running…

Avísale al humano; no inventes rodeos fuera de Orchemax. Los campos estructurados (capability, plan_have, plan_need) siguen en el error para herramientas; ya no son la frase que lee la persona.

Assist HITL (Team)#

Desde Team hacia arriba el overlook incluye Assist por Telegram: vincula un chat en /app/assist; /approve / /deny / /reject en el bot de Orchemax (o los botones Assist de la consola) escriben avisos HITL del tenant. Professional incluye Telegram avisos suaves (telegram_notify) sin Assist de org. Detalle: Notificaciones y aprobaciones.

Control en vivo desde la consola (Team)#

Cuatro valores llegan en vivo a una instalación vinculada, dentro del arriendo firmado, y se aplican en su siguiente refresco de derechos — nada se reinicia:

Valor Dónde se define Efecto
schedule.set /app/schedules (al guardar una ventana) Cuándo pueden despachar los agentes
quota.set /app/usage → Control en vivo Baja corridas/día, proyectos, agentes, paralelismo
models.allow /app/usage → Control en vivo Los únicos ids de modelo que sirve el gateway
webhook.set /app/settings → Webhooks de las máquinas A dónde publican sus propios eventos las máquinas

Dos reglas valen para los cuatro:

  • Un envío solo aprieta. El servidor rechaza un tope por encima de lo que permite el plan, y la instalación vuelve a recortarlo de su lado.
  • Un envío no es un ajuste local. Viaja en la firma del arriendo, así que el plan.enforce: false del taller no lo apaga, y editar el espejo de la máquina invalida el arriendo en vez de aflojar un tope.

webhook.set nunca lleva una credencial: se da el nombre de la variable de entorno que guarda el secreto de firma (ORCH_HOOK_SECRET), no su valor. El servidor rechaza un payload cuyas hojas parezcan una clave viva.

En la máquina, orch server entitlements trae el arriendo nuevo de inmediato en vez de esperar el siguiente sync.

Cómo se relaciona con el resto#

  • El ahorro del crush y los aciertos de caché aparecen en orch usage, como bytes que no salieron al cable y no como una afirmación.
  • Los proveedores se catalogan a nivel de usuario con orch gateway setup, que escribe ai.yaml en el machine home; un orch.yaml de workspace puede sobreescribir ai.providers.
  • Los secretos nunca van en el chat. La bóveda o una variable de entorno del usuario, y luego orch gateway.

Aquí no hay nada que ejecutar#

El gateway es opcional. Un agente con su propia llave funciona sin él; lo enciendes cuando quieres una sola conexión, rotación de llaves y crush en lugar de una configuración por proveedor por agente.