epic: Cortex as the egress proxy for MOCA's sandbox service (SBX)
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 5/5
- Tiempo estimado
- Más de una semana
- Aptitud para principiantes
- 25/100
- Tipo de issue
- Nueva funcionalidad
- Claridad
- Bastante claro
- Estado de actividad
- Activo
- Stack tecnológico
- go
- Área
- backend, networking, security
Línea de trabajo
Start with the linked MOCA MI1 §8 contract and §12.3 conformance suite, then review the listed Cortex entry point core/listener/forwardproxy/server.go:963-989 and plugin registration in core/plugins/README.md:60. First settle deployment shape A or B in a design session; the child issues and their named conformance cases define the implementation path. Done means Cortex passes MOCA's conformance suite and verifies the sandbox grant.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Goal
Make Cortex the egress proxy for MOCA's sandbox service (SBX): the one place where traffic from code running inside a sandbox leaves for the internet, where each user's real credentials are swapped in, and where Cortex's policy plugins (IBAC, SPARC, OPA, budgets, observability) run on that traffic.
This epic replaces #905. That issue's history (the Z5 per-subject injector, two rescopes, a gap analysis) is summarised here, so the design sessions can start from this page.
Where Cortex would sit
SBX runs every tool call in a fresh Firecracker microVM (ADR-0042, SBX0 design). The VMs have no network device. A guest's HTTPS_PROXY=http://127.0.0.1:3128 is spliced over vsock to a Unix socket per VM on the KVM host. One egress proxy per host is shared by every VM on it, so it serves many users at once.
guest (curl, pip, git, SDKs) KVM host internet
HTTPS_PROXY ─► vsock 1025 ─► per-VM Unix socket ─► egress proxy ─► allow list, SSRF ─► github.com …
(fd handed over by │ identity = the socket the
vmpool, SCM_RIGHTS) │ connection arrived on
└► (identity, host) → user's credential
Each guest holds only an inert placeholder (moca-placeholder). The proxy overwrites it with the right user's credential for the destination host.
The contract is MOCA MI1 §8, and its black-box test suite is §12.3.
The plan, and why Cortex is not on the critical path
MOCA ships its own small Go proxy, moca-egress, first (rossoctl/moca#583: transport slice, the control protocol, and a public conformance suite at remote-worker/egress/conformance). Cortex replaces it when it passes the same suite and MOCA needs a capability only Cortex has. Each child issue names the conformance case that proves it is done.
Not yet decided: two deployment shapes. These change how much each issue has to cover. Please settle them in the first design session:
- A. Cortex owns everything. Cortex accepts the per-VM sockets and does identity, the allow list and SSRF checks itself. Every issue below applies.
- B. MOCA front end, Cortex behind it. A small MOCA component owns the VM sockets, the connection → sandbox binding, the allow list and the SSRF check. It stamps a verified identity onto the plaintext CONNECT. Cortex trusts that identity only on the front end's socket. #1477, #1483 and #1484 move to MOCA, and #1476 shrinks.
Child issues
Priorities: P0 means no per-user injection without it. P1 means needed before any user. P2 means needed before many users. P3 is compatibility.
| Issue | Pri | Gap | In #905's breakdown? |
|---|---|---|---|
| #1476 | P0 | Caller identity from the connection, carried into every bridged request and every plugin | step 4 |
| #1477 | P0 | Per-VM Unix-socket listeners handed over at run time (SCM_RIGHTS), with a control channel | step 3 (Unix sockets only) |
| #1478 | P0 | Credential keyed by (identity, host), resolved from MOCA's token endpoint | step 1 |
| #1479 | P0 | Inject into the header the credential declares (Bearer, Basic for git, x-api-key), always overwriting | new |
| #1480 | P1 | Fail closed on hosts that have a credential: never fall back to an opaque tunnel | new |
| #1481 | P1 | Refuse to send a credential over plain HTTP (403 plaintext_credential) |
new |
| #1482 | P1 | Exactly one audit event per injection, in MOCA's audit shape | step 2 |
| #1483 | P1 | Deny-by-default destination allow list | new |
| #1484 | P1 | SSRF guard: resolve once, refuse private and metadata ranges, dial the checked address | new |
| #1485 | P2 | Per-VM connection cap and per-session request cap (429) |
new |
| #1486 | P2 | Session recording and the session API on a shared host | new |
| #1487 | P3 | Run body-reading plugins (IBAC, SPARC, tool-prune) under a streaming contract | new |
| #1488 | P3 | WebSocket upgrades on intercepted connections | new |
| #1489 | — | Verify MOCA's sandbox grant and pass the conformance suite | step 5 |
Suggested order: settle the A/B decision first. Then #1476 → #1478 → #1479, the injection path, which only becomes safe once #1476 lands. Then #1480, #1481 and #1482. #1483–#1488 depend on the A/B decision, and #1489 is the final gate.
Already matching
Nothing needs doing here:
- An inner
Hostthat differs from the CONNECT authority gets421(core/listener/forwardproxy/server.go:963-989). - The upstream certificate is verified before a leaf is minted.
- The plugin model fits. Plugins register in
init()behindinclude_plugin_<name>build tags (core/plugins/README.md:60), so MOCA can link its own identity or resolver plugins into a downstream build without forking.
References
- MOCA: MI1 §8 + §12.3, P4.1 egress transport,
proto/egress/v1/control.proto, the MOCA-side stub rossoctl/moca#241. - Cortex evidence: the code was read at
maind4394da(2026-10-22). It was read, not built or run against MOCA's contract. - Supersedes #905. Parent: rossoctl/rossoctl#2546 (Stream 1).
- Lenguaje dominante
- Go
- Estrellas
- 23
- Forks
- 44
- Merge medio
- 14 h 38 min
- PR fusionados (30 d)
- 254
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de rossoctl/cortex
-
feature: let `session.archive` set where it writes, so a second Cortex can keep its own sessionsAbiertoenhancement
Dificultad 4/5 3-5 días Aptitud para principiantes 54/100
Los mantenedores suelen responder en 1 día
-
feature: agentop observe — show a request and its response as one rowPosiblemente ocupada @huang195 la tomó hoy. Abiertoenhancement ux
rossoctl/cortex#1681 · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
feature: agentop observe — search user prompts and agent responses across a session's eventsPosiblemente ocupada @huang195 la tomó hoy. Abiertoenhancement ux
rossoctl/cortex#1680 · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
feedback laptop
Dificultad 5/5 Más de una semana Aptitud para principiantes 28/100
Los mantenedores suelen responder en 1 día
-
chore: Remove the decline_responses_websockets translation after a deprecation releasePosiblemente ocupada @Alan-Cha la tomó hace 4 días. Abierto
Dificultad 3/5 1-2 días Aptitud para principiantes 55/100
rossoctl/cortex#1625 · 1 asignado ·
Los mantenedores suelen responder en 1 día
Todos los issues de rossoctl/cortex
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
router-for-me/CLIProxyAPI#7330 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Los mantenedores suelen responder en 1 día
-
bug os:ios
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
hajimehoshi/ebiten#4039 · 2 comentarios ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
Los mantenedores suelen responder en 9 días