Arquitectura del núcleo de BeeverForge
La caja y el piso de calidad
Un job se ejecuta en una caja: un entorno que el worker crea para ese job y descarta después. La caja ejecuta la imagen propia del cliente con las herramientas de la plataforma montadas dentro, y se certifica contra los controles propios del proyecto antes de recoger cualquier ticket.
Tipos de caja#
| Tipo | Qué aísla |
|---|---|
container | El predeterminado. Un contenedor Docker desechable creado a partir de la imagen del proyecto. Limita CPU y memoria, lleva secretos solo durante el job y solo ve el clon montado, la caja de herramientas, las directrices y una caché. No aísla la red. |
worktree | Un git worktree en la propia máquina del worker. Solo aísla el estado del código: no aísla CPU, memoria, red ni secretos. El código lo describe como nunca apto para trabajo no confiable. |
fargate | Solo una descripción de una caja de tareas remota. Su ejecutor no está en este repositorio; lo proporciona un complemento. |
container tal como la inicia el worker. Los secretos se pasan durante el job; la red no está aislada.Un job durable se rechaza en una caja que no limita nada, salvo que el proyecto declare que el trabajo es del propio operador. Se pueden añadir más tipos como puntos de entrada box.<kind>.
Certificar la caja#
beeverforge box prove convierte la configuración de la caja en un hecho registrado (ADR-0037). Ejecuta una secuencia fija y se detiene en el primer fallo del que dependen las comprobaciones posteriores, de modo que cada causa se informa una sola vez, con su solución:
- La etiqueta de la imagen se resuelve en un digest.
- La caja de herramientas se comprueba contra la imagen: una imagen cuya biblioteca C difiere de la compilación de la caja de herramientas, como una imagen musl para una caja de herramientas glibc, no puede ejecutar los binarios del harness y se rechaza nombrando la causa.
- El contrato de la imagen: un shell POSIX en
/bin/sh, un proceso que la mantenga viva,git, unHOMEcon permiso de escritura y un/workspacecon permiso de escritura. Una imagen distroless o scratch no puede usarse. - La caja arranca, se ejecuta el
setup:del proyecto y luego sus controlesvalidate:sobre una copia intacta demain, dentro de la caja. - Se comprueba el harness: su versión, sus credenciales tal como se ven dentro de la caja, el almacén de confianza y si el endpoint del harness es accesible a través de la red de la caja.
beeverforge box prove, en el orden en que se ejecutan.En una caja que ejecuta la imagen del cliente, la certificación no pide nada a ningún agente y no gasta tokens del harness. En una caja sin imagen, donde el inicio de sesión del harness es una sesión en la máquina del operador, pide al harness una sola palabra para confirmar que el inicio de sesión funciona.
Una certificación es válida mientras no cambien el digest de la imagen, la caja de herramientas ni el hash de los comandos setup: y validate: del proyecto. En una caja con imagen, la caja de herramientas se identifica por su sistema operativo, su arquitectura y su biblioteca C; en una caja sin imagen, una nueva versión del harness deja obsoleta la certificación. Las certificaciones no caducan por tiempo. La exploración del tablero se niega a recoger trabajo de un proyecto cuya caja tiene algo que certificar y no tiene una certificación válida, e indica cuál de esos hechos cambió. Una caja worktree no tiene imagen; solo está sujeta a este control en un despliegue que declara BEEVERFORGE_OWN_WORK, el entorno donde el worker es la propia máquina del operador.
Un control no bloqueante que falla durante la certificación no detiene la recogida, salvo que su comando no haya podido ejecutarse en absoluto.
El montaje de la caja de herramientas#
Los binarios del harness no se instalan en la imagen del cliente. Están en una caja de herramientas que se monta en cada caja en modo de solo lectura en /opt/beeverforge-toolbox; las directrices del operador también se montan en solo lectura. El código indica el límite de esa opción: la solo lectura protege contra accidentes, no contra un adversario, porque un proceso que se ejecuta como root en la caja todavía puede copiar un binario fuera.
Tráfico de red saliente#
La red de la caja container es de forma predeterminada bridge de Docker, que da acceso completo a internet de salida. Todo lo que la red de la caja puede alcanzar es alcanzable por el código que escribió el agente.
box.networken el registro selecciona otra red de Docker, como una red restringida o una detrás de un proxy que proporcione el despliegue. El núcleo no tiene una lista de permitidos ni un proxy integrados.- Un job necesita como mínimo el endpoint del harness. Un proyecto en una plataforma Git remota necesita además la plataforma Git y el tracker, y todo lo que
setup:instale desde registros de paquetes. - La certificación comprueba que el endpoint del harness es accesible. Cuando un proyecto declara una identidad de máquina,
beeverforge doctorverifica si la caja puede llegar a la dirección de metadatos de instancia de la nube. - Un entorno aislado de la red no puede ejecutar un job, porque el agente de programación es un servicio remoto.
El piso de calidad#
El piso está en policy/floor.py: los roles de validación test y security son obligatorios. Antes de la pasada de implementación, después del dimensionamiento, el ejecutor comprueba que ambos roles están presentes después de la cascada de valores predeterminados del despliegue, presets del stack, el bloque de todo el repositorio y los bloques por componente. Un proyecto al que le falta uno se rechaza nombrando el rol y una solución; no se ejecuta nada.
- Todo proyecto hereda un control
securitydeorg_defaults/floor.yaml: un análisis de los archivos versionados en busca de credenciales incluidas en commits. Está declarado comoadvisory: true. - La herencia solo añade los roles que el proyecto no declara. Un proyecto que declara un rol lo conserva tal como está escrito, así que puede sustituir el comando de un rol obligatorio, o hacerlo no bloqueante, y seguir cumpliendo el piso. El piso comprueba que los roles existen, no lo que ejecutan.
- Las rutas protegidas son aditivas: un proyecto puede ampliar la lista del despliegue y no tiene ningún campo para quitar elementos. Un cambio que toca una ruta protegida nunca recibe un merge automático.
Qué hace advisory: true. El control se ejecuta y se informa, con una marca de advertencia en el cuerpo del pull request. Nunca hace fallar el job, nunca bloquea el merge y nunca provoca una reparación. Un perfil puede convertir un control no bloqueante en bloqueante, nunca al revés.
Ejecutor y revisor separados#
El revisor se ejecuta en un contexto propio. Recibe el ticket, el diff, los resultados de los controles y las restricciones, y nunca se le muestra cómo se hizo el cambio ni lo que el ejecutor dice que hizo. El revisor de Claude Code es una sesión nueva, no reanudada, en modo de planificación; al revisor genérico del harness se le indica que no escribió el código. El código señala que la independencia se reduce cuando el revisor y el ejecutor usan el mismo harness.
El diff es la verdad#
ADR-0001 lo formula así: «La etiqueta es una suposición; el diff es la verdad». La revisión recibe git diff desde la base medida hasta HEAD, calculado dentro de la caja. El resumen propio del ejecutor va en el pull request, marcado como una afirmación: ningún control lo verifica y no se muestra al revisor.
Respuestas selladas a los controles humanos#
Una persona aprueba una versión de producción, responde a un control de merge o dice «todavía no» en el panel, que comprueba quién llama antes de enviar nada. Como cualquier cosa que pueda llegar al motor podría enviar la misma señal, cada respuesta se sella con gate_seal.py:
- El sello es un HMAC-SHA256 sobre el tipo de control, el id del workflow, una caducidad, un nonce y exactamente los campos sobre los que actuará el workflow, como la versión y quien aprueba.
- Un sello es válido durante 600 segundos y para un solo workflow, y se consume una sola vez.
- El worker lo verifica en una actividad, con una comparación de tiempo constante, antes de publicar una versión o hacer un merge. Cualquier fallo cuenta como un no, se registra donde el panel lo muestra y deja el control pendiente.
- La clave es
BEEVERFORGE_GATE_SECRETcuando está definida; si no, un archivogate.keycreado con modo 0600, junto al registro o en~/.beeverforge/.
Límites que documenta el módulo. El sello prueba que un proceso que tiene la clave envió la respuesta, no que la envió el panel. La clave es simétrica, así que cualquier proceso que pueda leerla puede sellar; el panel y el rol de producto del worker son firmantes legítimos. El sello resiste frente a la caja container, que nunca ve la clave. No resiste frente a una caja worktree, que se ejecuta como el usuario del operador y puede leer el archivo de la clave, ni frente a los roles evaluadores, que en la instalación con compose se ejecutan como cajas worktree dentro del contenedor del worker. Resolverlo requiere un sello asimétrico.
Lo que el sello sí cambia: una vía no prevista hacia el motor puede leer y cancelar workflows, pero no puede aprobar una versión ni hacer el merge de un pull request.
Fuentes#
beeverforge/adapters/sandbox/registry.pybeeverforge/adapters/sandbox/container.pybeeverforge/adapters/sandbox/worktree.pybeeverforge/box_prove.pybeeverforge/runtime/temporal/activities.pybeeverforge/policy/floor.pybeeverforge/policy/conformance.pybeeverforge/org_defaults/floor.yamlbeeverforge/contracts/manifest.pybeeverforge/adapters/reviewer/base.pybeeverforge/adapters/reviewer/harness.pybeeverforge/gate_seal.py- ADR-0001, ADR-0037