Autenticación y principales
Cada conexión RPC con el daemon vincula un principal durante el protocolo de enlace initialize, y cada llamada a método se verifica frente a los permisos que la configuración actual asigna a ese principal. Esta página cubre el conjunto de proveedores, el registro de usuarios locales, los perfiles de permisos y, lo más importante, qué cambia para las conexiones remotas existentes.
El modelo en una sola pasada
-
Un proveedor verifica una sola credencial: un
auth_tokenexplícito del handshake selecciona el proveedor indicado enauth_provider(por defectonative, el token de emparejamiento de la pasarela), y sin token un socket Unix local presenta su uid de par del kernel al proveedorpeercred. El rechazo de un proveedor seleccionado es definitivo: una credencial nunca se reintenta con otro proveedor. -
El resolver compartido asigna la identidad verificada a un id de principal canónico y a los perfiles de permisos que la configuración le asigna. Las identidades OIDC se indexan por el emisor + sujeto validados, y las identidades del roster local por su id de principal duradero
[users.<name>]. -
Todos los métodos RPC excepto los dos métodos de enlace (handshake) se clasifican en un permiso requerido de recurso-verbo y se rechazan sin él.
initializetransporta la propia credencial, ycert/renewse autentica mediante el certificado de cliente TLS mutuo que lo presenta, no mediante un principal. Los selectores de grano fino se componen sobre estos:- config writes check
config_write_paths; session/newysession/promptcomprueban el selector de agente y limitan el espacio de trabajo de la sesión a un directorio que la política de ese agente permite leer y escribir, ya sea que el espacio de trabajo lo indique la solicitud, esté almacenado con una sesión reanudada o se restaure desde una sesión duradera. La sesión queda vinculada al directorio resuelto, no a la forma escrita en la solicitud, por lo que cambiar el destino de uncwdcon enlace simbólico después de la admisión no la mueve, y una sesión activa que use ese alias se rechaza. Los demás métodos de sesión todavía no comprueban el agente, como se describe en Lo que esta capa no hace (todavía);- ejecutar o aprobar un SOP requiere todos los agentes con los que se ejecuta, con la misma regla de selector de herramientas que una sesión, y crear, guardar o eliminar uno requiere los agentes con los que se ejecuta. Un paso que no nombra ningún agente, ni en el paso ni en el procedimiento, cuenta como el primer alias de agente configurado en orden de clasificación, que es el agente al que recurre el ejecutor headless. Se cuentan todos los pasos, incluidos los de un procedimiento determinista;
- Los adjuntos, los archivos de personalidad, las consultas de costes que nombran un agente y los trabajos cron verifican el selector de agente. Un adjunto enviado por ruta local también debe indicar una ruta absoluta que la política del agente destinatario le permita leer. Un resumen de costes de la flota solo enumera los agentes del principal en su desglose por agente, pero sus totales y su uso por modelo siguen abarcando a todos los agentes;
fs/list_dirlista únicamente las rutas absolutas que la política de un agente habilitado, que el principal puede usar, permite leer a ese agente. Rechaza rutas relativas, componentes..y, en Windows, rutas de red y de dispositivo. Estas raíces provienen de la política resuelta de cada agente: su perfil de riesgo, los espacios de trabajo hermanos a los queworkspace.accessconcede acceso de lectura (lectura y escritura, en el caso de un espacio de trabajo de sesión) y el directorio compartido de skills, que es de solo lectura. Un agente que no está restringido al espacio de trabajo (autonomíafull, un perfil de riesgo conworkspace_only = false, el preajusteyolooworkspace.unrestricted_filesystem = true) puede leer cualquier ruta fuera de sus rutas prohibidas, por lo que un principal autorizado para usar dicho agente puede listar y abrir sesiones en cualquier lugar al que ese agente pueda acceder.
session/newysession/promptrepiten estas comprobaciones después de esperar la cola de la sesión, de modo que una solicitud encolada antes de que se restringiera su principal o caducara su credencial se rechaza cuando le llega su turno. - config writes check
La autorización está activa para las ediciones realizadas mediante los métodos RPC de configuración del daemon, que es lo que usa el editor de configuración de zerocode: al editar [permission_profiles], [users], [oidc] o security.trust_daemon_uid de esta forma, la política se recompila en el momento de guardar. Las conexiones establecidas con token nativo y las conexiones locales se vuelven a resolver en su siguiente operación, sin necesidad de reconectar ni reiniciar, y una conexión OIDC debe inicializarse de nuevo. Las ediciones realizadas fuera del daemon, directamente en config.toml, a través del panel web o con zeroclaw config set, se aplican en la siguiente recarga o reinicio del daemon. Revocar un token de emparejamiento del gateway mediante los controles de emparejamiento del gateway invalida las conexiones autenticadas con él antes de su siguiente operación. Eliminar un token de gateway.paired_tokens editando la configuración, por RPC o en disco, no tiene ningún efecto sobre el daemon en ejecución, y las conexiones que usan ese token siguen autorizadas. La eliminación se aplica en la siguiente recarga o reinicio del daemon, a menos que, antes de ese momento, un cambio de emparejamiento realizado a través del gateway escriba de nuevo en la configuración el conjunto de tokens activos, incluido ese token. Revoca los tokens mediante los controles de emparejamiento.
Proveedores
| Proveedor | Credencial | Configurado por |
|---|---|---|
native | Token de portador de emparejamiento de la puerta de enlace | Emparejamiento con la puerta de enlace (/pair); el daemon y la puerta de enlace comparten una única autoridad de token activa |
peercred | UID del par en el socket local (Unix) | Siempre activo; [users.<name>].uid asigna un uid a un principal con nombre |
oidc.<alias> | JWT o token bearer opaco de tu IdP | [oidc.<alias>] |
Conexiones locales
En un socket Unix, una conexión que no presenta token se identifica por su UID de par del kernel. Mientras security.trust_daemon_uid sea true (el valor por defecto), el UID propio del daemon se conecta como operador compartido de confianza con acceso completo, con o sin una lista de [users], de modo que una instalación sin lista se comporta como antes para la cuenta que ejecuta el daemon.
El operador que ejecuta el daemon es dueño de su archivo de configuración, y la recuperación de bloqueos solo local depende de esa autoridad. En un socket Unix, establezca security.trust_daemon_uid en false para exigir que todo par local, incluido el uid del propio daemon, se asigne a través del roster o presente un token. La opción no tiene efecto en una tubería con nombre de Windows, como se describe a continuación.
Cualquier otro uid debe estar asignado mediante una entrada explícita [users.<name>].uid. Un uid no asignado (incluido root) que no presente ningún token es denegado, exista o no un roster; no hay ningún mecanismo de respaldo al acceso de operador compartido. El listener crea el socket con permisos solo para el propietario, así que hoy solo la cuenta propia del demonio y root pueden acceder a él; una entrada del roster decide lo que cualquier otro uid puede hacer una vez que pueda hacerlo.
Los named pipes de Windows no transportan el uid del par. Sin roster, la ACL del pipe es la credencial y una conexión local es el operador compartido. Una vez que existe un roster [users], un cliente local en ese roster debe presentar un token, security.trust_daemon_uid no tiene efecto y la cuenta propia del daemon no tiene una ruta local de confianza. Un token de gateway emparejado sigue autenticando como el operador compartido mientras la política se compila, por lo que un cliente que lo envíe, por WSS o por el pipe, puede reparar el roster en vivo. La conexión local por pipe de zerocode no envía ningún token, así que sin un cliente de ese tipo, un bloqueo se repara editando config.toml y reiniciando.
Un cliente puede reenviar el entorno de su shell en initialize para que los subprocesos del daemon vean su PATH y sus sockets de credenciales (véase Paso de variables de entorno). Esa instantánea solo se conserva para un principal de nivel operador (el operador compartido, o un principal del roster con admin = true) en una conexión local. Todos los demás principales, y cualquier conexión remota, reciben en su lugar el entorno propio del daemon.
La elegibilidad se vuelve a comprobar al crear o restaurar una sesión. Una sesión existente que conserva un entorno reenviado no vacío solo puede reanudarse o recibir prompts por un operador local autorizado en ese momento. Reconectarse por WSS, o perder admin mientras hay un prompt en cola, no traslada ese entorno al siguiente turno: la solicitud se rechaza antes de ejecutarse. Para continuar sin el entorno reenviado, crea una nueva sesión. Las sesiones sin valores reenviados siguen siendo elegibles para la reanudación normal, sujeta a las demás comprobaciones de autorización. El entorno conservado es inmutable durante la vida de su sesión; rechazar una solicitud posterior no lo reescribe bajo un turno que ya se está ejecutando. Reanudar también requiere que el entorno autorizado de la conexión actual coincida con el entorno conservado de la sesión. Si difiere, crea una nueva sesión; una sesión sin entorno puede reanudarse aunque se haya quitado admin.
Recuperación
Nunca se ofrece un bypass de autenticación remota: una conexión remota siempre debe presentar una credencial válida. Mientras la política de autorización siga compilando, un cliente que posea un token de gateway emparejado o un principal de nivel operador puede reparar un bloqueo desde cualquier lugar. Sin tal credencial, y siempre en el estado de denegación total, la vía de recuperación se ejecuta en el host que ejecuta el daemon. Qué ruta local se aplica depende de si la política sigue compilando.
Bloqueado por una política que sí compila. La autorización está activa y la ruta local de confianza sigue intacta en un socket Unix; en Windows, consulte la nota sobre la named pipe más arriba. Conéctese localmente con el uid del propio demonio: con security.trust_daemon_uid = true (el valor predeterminado), esa cuenta es el operador compartido de confianza, independientemente de lo que indique la lista de usuarios. Repare la entrada incorrecta a través de esa conexión local, por ejemplo en el editor de configuración de zerocode o con un RPC config/set de users.alice.uid, y el cambio se compila y se publica al guardar. Editar config.toml o ejecutar zeroclaw config set también lo repara, pero solo una vez que el demonio se recargue o se reinicie.
Bloqueado por un estado aceptado de denegación total. La política no se compiló, por lo que el estado aceptado rechaza a todos los principales antes de que se ejecute la resolución, incluidos el uid propio del daemon y el operador compartido. Ningún RPC puede repararlo: config/set y config/reload también son rechazados, junto con todo lo demás. Edite config.toml directamente como su propietario y luego reinicie el daemon para que las secciones reparadas se compilen y publiquen. El daemon ignora SIGHUP, por lo que el reinicio es el paso que lo recarga.
Si security.trust_daemon_uid se establece en false, la ruta de UID de confianza desaparece. Una política que compila aún puede repararse en vivo por un cliente que presente un token de gateway emparejado, o por un principal del roster con permisos de administrador; en caso contrario, ambos estados se reparan de la misma forma: editar config.toml como su propietario y reiniciar. Desactive la configuración solo donde sea aceptable.
Lista de usuarios
permission_profiles
La entrada [permission_profiles.<alias>] que concede los permisos de este usuario, combinada mediante unión determinista. Obligatoria; un usuario sin perfil no puede autenticarse.
Colócalo sobre cualquier superficie:
Panel de control del gateway
Abre /config/users y define el campo users.<alias>.permission_profiles.
zerocode
En el panel Config, establece el campo users.<alias>.permission_profiles.
zeroclaw config
zeroclaw config set users.<alias>.permission_profiles <value>
Variable de entorno
Exporte la anulación (shells POSIX; colóquelo en ~/.bashrc, ~/.zshrc, .env o un Dockerfile). Reemplace <alias> con el alias literal:
export ZEROCLAW_users__<alias>__permission_profiles=
principal_id
Identificador duradero del principal para esta entrada; por defecto es el nombre de la entrada. Los registros de auditoría usan este id como clave, NO el nombre de la entrada, y la propiedad de las sesiones, la memoria y las aprobaciones se asociará a él cuando llegue el almacenamiento propiedad del principal. Para renombrar la entrada sin dejar huérfanos sus datos, establezca principal_id con el id original en la misma edición. Cambiar el id de principal efectivo de una entrada crea un nuevo principal.
Colócalo sobre cualquier superficie:
Panel de control del gateway
/config/users para abrir y establece el campo users.<alias>.principal_id.
Wait, I should fix that: the translation should read naturally in Spanish.
Abre /config/users y establece el campo users.<alias>.principal_id.
zerocode
En el panel Config, establece el campo users.<alias>.principal_id.
zeroclaw config
zeroclaw config set users.<alias>.principal_id <value>
Variable de entorno
Exporte la anulación (shells POSIX; colóquelo en ~/.bashrc, ~/.zshrc, .env o un Dockerfile). Reemplace <alias> con el alias literal:
export ZEROCLAW_users__<alias>__principal_id=
uid
Uid de Unix aceptado para este usuario a través del socket local (credencial de par). Requerido actualmente: es la única credencial del roster que admite el conjunto de proveedores aceptados.
Colócalo sobre cualquier superficie:
Panel de control del gateway
Abre /config/users y establece el campo users.<alias>.uid.
zerocode
En el panel Config, establece el campo users.<alias>.uid.
zeroclaw config
zeroclaw config set users.<alias>.uid <value>
Variable de entorno
Exporte la anulación (shells POSIX; colóquelo en ~/.bashrc, ~/.zshrc, .env o un Dockerfile). Reemplace <alias> con el alias literal:
export ZEROCLAW_users__<alias>__uid=
El nombre de la entrada funciona también como id de principal duradero, salvo que principal_id fije uno explícitamente. Los registros de auditoría se indexan por ese id actualmente. Las sesiones, la memoria y las aprobaciones todavía no se indexan por él (véase Lo que esta capa no hace (todavía)), pero sí lo harán, así que para renombrar una entrada sin dejar huérfanos sus datos más adelante, establezca principal_id con el id original en la misma edición.
OIDC
Cada entrada [oidc.<alias>] representa una relación de confianza con un emisor; la verificación de tokens (JWKS sin conexión o introspección RFC 7662), el mapeo de claims y los límites de vida útil se documentan en la referencia de la sección:
actor_claim
Ruta con puntos a una claim que el emisor incluye en los tokens de exactamente UN tipo de actor (presente, no nula), p. ej. gty de Auth0 en los tokens de client-credentials o uid de Okta en los tokens de usuario. actor_claim_marks indica el tipo de actor que demuestra su presencia; su ausencia demuestra el otro tipo. Obligatorio para un cliente que emite tokens tanto humanos como de máquina (declarado en ninguna de las dos listas de clientes); también se verifica frente a los clientes declarados, donde la evidencia contradictoria deniega. Vacío = sin clasificación basada en claims, por lo que todo cliente aceptado debe estar declarado.
Colócalo sobre cualquier superficie:
Panel de control del gateway
Abra /config/oidc y establezca el campo oidc.<alias>.actor_claim.
zerocode
En el panel Config, establece el campo oidc.<alias>.actor_claim.
zeroclaw config
zeroclaw config set oidc.<alias>.actor_claim <value>
Variable de entorno
Exporte la anulación (shells POSIX; colóquelo en ~/.bashrc, ~/.zshrc, .env o un Dockerfile). Reemplace <alias> con el alias literal:
export ZEROCLAW_oidc__<alias>__actor_claim=
actor_claim_marks
Qué tipo de actor demuestra la presencia de actor_claim.
Colócalo sobre cualquier superficie:
Panel de control del gateway
Abra /config/oidc y establezca el campo oidc.<alias>.actor_claim_marks.
zerocode
En el panel Config, establece el campo oidc.<alias>.actor_claim_marks.
zeroclaw config
zeroclaw config set oidc.<alias>.actor_claim_marks <value>
Variable de entorno
Exporte la anulación (shells POSIX; colóquelo en ~/.bashrc, ~/.zshrc, .env o un Dockerfile). Reemplace <alias> con el alias literal:
export ZEROCLAW_oidc__<alias>__actor_claim_marks=
allowed_authorized_parties
Valores azp (authorized party) permitidos, es decir, el cliente al que se emitió el token. Vacío = sin restricción; no vacío = el token debe incluir una claim azp que figure aquí, o la autenticación falla de forma segura (fail closed).
Colócalo sobre cualquier superficie:
Panel de control del gateway
Abre /config/oidc y define el campo oidc.<alias>.allowed_authorized_parties.
zerocode
En el panel Config, establece el campo oidc.<alias>.allowed_authorized_parties.
zeroclaw config
zeroclaw config set oidc.<alias>.allowed_authorized_parties <value>
Variable de entorno
Exporte la anulación (shells POSIX; colóquelo en ~/.bashrc, ~/.zshrc, .env o un Dockerfile). Reemplace <alias> con el alias literal:
export ZEROCLAW_oidc__<alias>__allowed_authorized_parties=
audience
Audiencia que el token debe incluir en su claim aud para este daemon (normalmente el ID de cliente o el identificador de recurso registrado en el IdP). Obligatorio para la verificación del token.
Colócalo sobre cualquier superficie:
Panel de control del gateway
Abre /config/oidc y establece el campo oidc.<alias>.audience.
zerocode
En el panel Config, establece el campo oidc.<alias>.audience.
zeroclaw config
zeroclaw config set oidc.<alias>.audience <value>
Variable de entorno
Exporte la anulación (shells POSIX; colóquelo en ~/.bashrc, ~/.zshrc, .env o un Dockerfile). Reemplace <alias> con el alias literal:
export ZEROCLAW_oidc__<alias>__audience=
claim_path
Ruta con puntos hasta la claim verificada que contiene los valores de rol/grupo de este despliegue (p. ej., realm_access.roles, groups). Debe configurarse explícitamente: el daemon se niega a adivinar dónde residen los permisos en un token.
Colócalo sobre cualquier superficie:
Panel de control del gateway
Abre /config/oidc y establece el campo oidc.<alias>.claim_path.
zerocode
En el panel Config, establezca el campo oidc.<alias>.claim_path.
zeroclaw config
zeroclaw config set oidc.<alias>.claim_path <value>
Variable de entorno
Exporte la anulación (shells POSIX; colóquelo en ~/.bashrc, ~/.zshrc, .env o un Dockerfile). Reemplace <alias> con el alias literal:
export ZEROCLAW_oidc__<alias>__claim_path=
client_id
Identificador de cliente con el que este daemon se autentica para flujos confidenciales (introspección de tokens; inscripción en una fase posterior). Por defecto es audience cuando está vacío.
Colócalo sobre cualquier superficie:
Panel de control del gateway
Abre /config/oidc y define el campo oidc.<alias>.client_id.
zerocode
En el panel Config, establezca el campo oidc.<alias>.client_id.
zeroclaw config
zeroclaw config set oidc.<alias>.client_id <value>
Variable de entorno
Exporte la anulación (shells POSIX; colóquelo en ~/.bashrc, ~/.zshrc, .env o un Dockerfile). Reemplace <alias> con el alias literal:
export ZEROCLAW_oidc__<alias>__client_id=
client_secret 🔑
Secreto del cliente para flujos de cliente confidencial (introspección de tokens). No es necesario para la validación con JWKS. Cifrado en reposo.
Colócalo sobre cualquier superficie:
Panel de control del gateway
Abre /config/oidc y define el campo oidc.<alias>.client_secret.
zerocode
En el panel Config, establece el campo oidc.<alias>.client_secret.
zeroclaw config
zeroclaw config set oidc.<alias>.client_secret # entrada enmascarada, almacenada cifrada
Variable de entorno
Exporte la anulación (shells POSIX; colóquelo en ~/.bashrc, ~/.zshrc, .env o un Dockerfile). Reemplace <alias> con el alias literal:
export ZEROCLAW_oidc__<alias>__client_secret=
interactive_clients
Identidades de cliente cuyos tokens son SIEMPRE de principales humanos (clientes de código de autorización / código de dispositivo), comparadas con la claim verificada client_id. Sus tokens deben incluir un sub no vacío y resolverse mediante profile_map. Un cliente solo puede aparecer en uno solo de service_clients e interactive_clients.
Colócalo sobre cualquier superficie:
Panel de control del gateway
Abra /config/oidc y establezca el campo oidc.<alias>.interactive_clients.
zerocode
En el panel Config, establece el campo oidc.<alias>.interactive_clients.
zeroclaw config
zeroclaw config set oidc.<alias>.interactive_clients <value>
Variable de entorno
Exporte la anulación (shells POSIX; colóquelo en ~/.bashrc, ~/.zshrc, .env o un Dockerfile). Reemplace <alias> con el alias literal:
export ZEROCLAW_oidc__<alias>__interactive_clients=
issuer
URL del emisor exactamente como aparece en los iss claims de los tokens validados (por ejemplo, https://sso.example.com/realms/main). El descubrimiento se obtiene de <issuer>/.well-known/openid-configuration y su campo issuer debe coincidir exactamente con este valor.
Colócalo sobre cualquier superficie:
Panel de control del gateway
Abre /config/oidc y define el campo oidc.<alias>.issuer.
zerocode
En el panel Config, establece el campo oidc.<alias>.issuer.
zeroclaw config
zeroclaw config set oidc.<alias>.issuer <value>
Variable de entorno
Exporte la anulación (shells POSIX; colóquelo en ~/.bashrc, ~/.zshrc, .env o un Dockerfile). Reemplace <alias> con el alias literal:
export ZEROCLAW_oidc__<alias>__issuer=
max_auth_lifetime_secs
Tiempo de vida máximo de autenticación (en segundos) para los tokens validados sin conexión (JWKS). La validación sin conexión no puede detectar revocaciones, por lo que la identidad caduca en EL MÁS TEMPRANO entre exp del token e iat + este límite. Debe ser > 0.
Colócalo sobre cualquier superficie:
Panel de control del gateway
Abra /config/oidc y establezca el campo oidc.<alias>.max_auth_lifetime_secs.
zerocode
En el panel Config, establece el campo oidc.<alias>.max_auth_lifetime_secs.
zeroclaw config
zeroclaw config set oidc.<alias>.max_auth_lifetime_secs <value>
Variable de entorno
Exporte la anulación (shells POSIX; colóquelo en ~/.bashrc, ~/.zshrc, .env o un Dockerfile). Reemplace <alias> con el alias literal:
export ZEROCLAW_oidc__<alias>__max_auth_lifetime_secs=
profile_map
Asigna un valor de claim encontrado en claim_path a un nombre de [permission_profiles.<alias>]. Los valores de claim sin asignación no conceden ningún permiso; una identidad que no se asigna a ningún perfil queda denegada.
Colócalo sobre cualquier superficie:
Panel de control del gateway
/config/oidc abre y establece el campo oidc.<alias>.profile_map.
zerocode
En el panel Config, establece el campo oidc.<alias>.profile_map.
zeroclaw config
zeroclaw config set oidc.<alias>.profile_map <value>
Variable de entorno
Exporte la anulación (shells POSIX; colóquelo en ~/.bashrc, ~/.zshrc, .env o un Dockerfile). Reemplace <alias> con el alias literal:
export ZEROCLAW_oidc__<alias>__profile_map=
require_at_jwt
Exigir el perfil de JWT tipado de RFC 9068 para la validación de JWKS. Los tokens opacos siguen siendo válidos únicamente mediante la introspección configurada.
Colócalo sobre cualquier superficie:
Panel de control del gateway
/config/oidc abre y establece el campo oidc.<alias>.require_at_jwt.
zerocode
En el panel Config, establece el campo oidc.<alias>.require_at_jwt.
zeroclaw config
zeroclaw config set oidc.<alias>.require_at_jwt <value>
Variable de entorno
Exporte la anulación (shells POSIX; colóquelo en ~/.bashrc, ~/.zshrc, .env o un Dockerfile). Reemplace <alias> con el alias literal:
export ZEROCLAW_oidc__<alias>__require_at_jwt=
require_mfa
Exigir que el token acredite MFA mediante el marcador agregado mfa del IdP o un ACR configurado aceptado antes de que la autenticación se realice correctamente.
Colócalo sobre cualquier superficie:
Panel de control del gateway
Abra /config/oidc y establezca el campo oidc.<alias>.require_mfa.
zerocode
En el panel Config, establece el campo oidc.<alias>.require_mfa.
zeroclaw config
zeroclaw config set oidc.<alias>.require_mfa <value>
Variable de entorno
Exporte la anulación (shells POSIX; colóquelo en ~/.bashrc, ~/.zshrc, .env o un Dockerfile). Reemplace <alias> con el alias literal:
export ZEROCLAW_oidc__<alias>__require_mfa=
required_acr
Valores acr (clase de contexto de autenticación) aceptables. Vacío = sin requisito; no vacío = la claim acr del token debe ser uno de estos valores, o la autenticación falla de forma segura (fail closed).
Colócalo sobre cualquier superficie:
Panel de control del gateway
Abre /config/oidc y establece el campo oidc.<alias>.required_acr.
zerocode
En el panel Config, establece el campo oidc.<alias>.required_acr.
zeroclaw config
zeroclaw config set oidc.<alias>.required_acr <value>
Variable de entorno
Exporte la anulación (shells POSIX; colóquelo en ~/.bashrc, ~/.zshrc, .env o un Dockerfile). Reemplace <alias> con el alias literal:
export ZEROCLAW_oidc__<alias>__required_acr=
revalidation_secs
Intervalo de revalidación (segundos) para el modo de introspección: el plazo registrado en cada identidad tras el cual la siguiente operación privilegiada debe volver a introspeccionar o fallar en modo seguro. 0 = revalidar en cada operación privilegiada.
Colócalo sobre cualquier superficie:
Panel de control del gateway
Abra /config/oidc y establezca el campo oidc.<alias>.revalidation_secs.
zerocode
En el panel Config, establece el campo oidc.<alias>.revalidation_secs.
zeroclaw config
zeroclaw config set oidc.<alias>.revalidation_secs <value>
Variable de entorno
Exporte la anulación (shells POSIX; colóquelo en ~/.bashrc, ~/.zshrc, .env o un Dockerfile). Reemplace <alias> con el alias literal:
export ZEROCLAW_oidc__<alias>__revalidation_secs=
service_clients
Identidades de cliente cuyos tokens son SIEMPRE principales de servicio (llamadores de client_credentials), con coincidencia frente a la reclamación client_id verificada del token. Los principales de servicio se identifican por emisor + identidad de cliente y nunca heredan suposiciones de usuario humano, sea cual sea la forma específica del proveedor que adopte su sub (<client>@clients, un ID de usuario de cuenta de servicio o el propio ID de cliente). La clasificación de actores es declarativa, nunca se infiere de la relación entre sub y client_id: un token es un servicio porque su cliente aparece listado aquí, es un humano porque su cliente está listado en interactive_clients, o se clasifica mediante actor_claim. Se deniega cualquier token cuyo cliente no esté declarado en ningún sitio y que no contenga evidencia de la reclamación de actor configurada. Un cliente solo puede aparecer en una de las dos listas.
Colócalo sobre cualquier superficie:
Panel de control del gateway
Abre /config/oidc y establece el campo oidc.<alias>.service_clients.
zerocode
En el panel Config, establece el campo oidc.<alias>.service_clients.
zeroclaw config
zeroclaw config set oidc.<alias>.service_clients <value>
Variable de entorno
Exporte la anulación (shells POSIX; colóquelo en ~/.bashrc, ~/.zshrc, .env o un Dockerfile). Reemplace <alias> con el alias literal:
export ZEROCLAW_oidc__<alias>__service_clients=
service_profile_map
Asigna el client_id verificado de un cliente de SERVICIO a un nombre de [permission_profiles.<alias>]. Los principales de servicio se resuelven ÚNICAMENTE a través de este mapa, nunca a través de profile_map, de modo que una credencial de máquina no pueda heredar un perfil humano a partir de reclamaciones con nombres similares. Un client_id de servicio sin entrada aquí queda autenticado, pero no tiene ningún permiso asignado (fallo cerrado).
Colócalo sobre cualquier superficie:
Panel de control del gateway
Abra /config/oidc y establezca el campo oidc.<alias>.service_profile_map.
zerocode
En el panel Config, establece el campo oidc.<alias>.service_profile_map.
zeroclaw config
zeroclaw config set oidc.<alias>.service_profile_map <value>
Variable de entorno
Exporte la anulación (shells POSIX; colóquelo en ~/.bashrc, ~/.zshrc, .env o un Dockerfile). Reemplace <alias> con el alias literal:
export ZEROCLAW_oidc__<alias>__service_profile_map=
validation
Cómo se validan los tokens presentados.
Colócalo sobre cualquier superficie:
Panel de control del gateway
Abre /config/oidc y define el campo oidc.<alias>.validation.
zerocode
En el panel Config, establece el campo oidc.<alias>.validation.
zeroclaw config
zeroclaw config set oidc.<alias>.validation <value>
Variable de entorno
Exporte la anulación (shells POSIX; colóquelo en ~/.bashrc, ~/.zshrc, .env o un Dockerfile). Reemplace <alias> con el alias literal:
export ZEROCLAW_oidc__<alias>__validation=
Inscripción (obtención de un token para presentar)
El daemon solo verifica los tokens; los clientes los obtienen del IdP. Se incluyen dos flujos sin navegador con la CLI:
# Inicio de sesión interactivo mediante la concesión de autorización de dispositivo (RFC 8628):
# prints a verification code to enter in any browser, waits for
# aprobación, luego escribe el token de acceso en la salida estándar.
export ZEROCLAW_AUTH_TOKEN=$(zeroclaw oidc login corp)
# Igual, mediante el navegador del sistema: código de autorización + PKCE (solo S256)
# with an RFC 8252 one-shot loopback listener. The mechanisms never
# recaer el uno en el otro.
export ZEROCLAW_AUTH_TOKEN=$(zeroclaw oidc login corp --browser)
# Principales de servicio sin interfaz mediante client_credentials (requiere el
# entryую's client_secret):
export ZEROCLAW_AUTH_TOKEN="$(zeroclaw oidc token corp)"
Los mensajes de progreso van a stderr; stdout contiene solo el token, por lo que ambos comandos se pueden componer con la sustitución de comandos (los comandos oidc se ejecutan antes de cualquier preámbulo de arranque que pudiera imprimir, incluida la revelación de la semilla OTP). No se almacena nada: presente el token como auth_token en el handshake RPC (o mediante la variable de entorno) antes de que expire y, a continuación, vuelva a inscribirse.
El cliente solo confía en el emisor que indica la entrada, y en ningún otro: el documento de descubrimiento debe declarar exactamente ese emisor antes de que se utilice cualquier endpoint que anuncie; todo endpoint que reciba una credencial debe cumplir la misma política de URL que el emisor (https, o http solo para un host de loopback exacto); nunca se siguen las redirecciones; los cuerpos de respuesta tienen un tamaño máximo; y una respuesta de token solo se acepta si contiene un token de acceso Bearer no vacío. Un cliente confidencial (una entrada con un client_secret) se autentica con HTTP Basic en cada solicitud; un cliente público envía su client_id en el formulario.
--browser abre el navegador del sistema automáticamente en macOS y Linux, y en todas las plataformas también imprime la URL de inicio de sesión para que puedas abrirla manualmente en un navegador de la misma máquina (la devolución de llamada llega a un puerto de bucle invertido del host que ejecuta la CLI). El lanzador del navegador se ejecuta desvinculado de los flujos estándar de la CLI, por lo que cualquier cosa que un lanzador escriba en su propia salida estándar no puede contaminar el resultado: stdout sigue contENIENDO solo el token.
Ambos adaptadores de callback, el listener de loopback de la CLI y el callback de la gateway, aplican la comprobación de emisor de RFC 9207. Una respuesta cuyo parámetro iss no coincide con el emisor que inició el flujo se rechaza antes de actuar sobre su code o su error. Una respuesta que no incluye ningún iss se acepta, ya que el parámetro es opcional y no todos los emisores lo envían. Cuando el intercambio de código devuelve un id_token junto al access token, ese id_token se valida por completo (emisor, audiencia, caducidad y el nonce vinculado a este flujo) y después se descarta. Solo el access token se presenta al daemon. El cliente de inscripción aplica la misma desconfianza al propio descubrimiento: rechaza un documento .well-known/openid-configuration cuyo issuer no coincida exactamente con el emisor configurado, incluida la barra final, antes de usar cualquier endpoint que mencione (la comprobación de RFC 8414). Además, limita el tamaño de los documentos que lee, de modo que un emisor hostil o defectuoso no pueda redirigir el flujo a endpoints de su elección ni responder con un cuerpo sin límite.
Los clientes que no tienen credenciales del IdP (el panel web, zerocode) se inscriben a través de la puerta de enlace, que replica los mismos flujos usando las credenciales de cliente de la entrada configurada: GET /api/oidc/providers lista los alias, POST /api/oidc/{alias}/device/start y /device/poll gestionan el flujo de dispositivo, y GET /oidc/login/{alias} ejecuta el flujo en el navegador, cuya página de callback de un solo uso entrega el token a la ventana que la abrió mediante postMessage (solo del mismo origen), con copia manual como alternativa. La puerta de enlace también envía Cross-Origin-Opener-Policy: same-origin, que elimina window.opener una vez que la ventana emergente ha navegado a través del proveedor de identidad. Por ello, en los navegadores actuales, la copia manual en esa página es el traspaso que funciona hoy, y el contrato de postMessage ya está definido para el siguiente trabajo en el panel web. Estas rutas no requieren autenticación por necesidad (la inscripción precede a la autenticación), tienen límite de frecuencia y no conceden ningún permiso: solo retransmiten lo que el IdP concede después de que el usuario apruebe. Justificación del diseño y tabla de modos de fallo: docs/security/oidc-browser-pkce-design-8289.md en el repositorio.
Los límites de tasa funcionan por capas. Las solicitudes que inician un flujo (POST /api/oidc/{alias}/device/start, GET /oidc/login/{alias} y GET /oidc/callback) cuentan contra una instancia específica de inscripción del limitador de fuerza bruta de la puerta de enlace: los mismos umbrales y el mismo bloqueo que rigen un token de emparejamiento incorrecto, pero en su propio registro. Así, una dirección bloqueada para la inscripción aún puede emparejarse o presentar una firma de webhook, y un bloqueo obtenido en cualquiera de esos casos nunca bloquea la inscripción. Un callback cuenta como intento solo cuando resulta improductivo: no hay estado de flujo activo para el state que transporta, el issuer no coincide, el proveedor de identidad devuelve un error, no hay código de autorización, el alias se eliminó mientras el flujo estaba en curso o falla el intercambio de código. Un callback que completa un inicio de sesión no tiene costo, como tampoco lo tiene uno que la puerta de enlace rechaza por tener su capacidad de retransmisión en uso, de modo que una multitud que inicia sesión al mismo tiempo no puede bloquear su dirección compartida. Un inicio de sesión en el navegador que se rechaza porque el almacén de flujos pendientes está lleno tampoco le cuesta nada al llamador, por la misma razón.
Las listas de proveedores, los sondeos de dispositivo y los inicios de sesión tienen un presupuesto por cliente de 20 solicitudes por minuto, lo que deja margen sobre el intervalo mínimo de sondeo de cinco segundos de RFC 8628 (12 sondeos por minuto) sin permitir que un cliente itere en bucle. Un sondeo cuenta como intento cuando el proveedor de identidad responde slow_down o rechaza el código de dispositivo directamente, de modo que un cliente que retransmite códigos de dispositivo basura cae en el bloqueo existente en lugar de sondear indefinidamente. Un fallo de transporte en el tramo de la puerta de enlace hacia el proveedor de identidad no dice nada sobre el llamador y no se le carga.
Un cliente que supera su presupuesto, o que está bloqueado, recibe HTTP 429 con un Retry-After que indica la demora en segundos. zerocode espera al menos esa demora, y nunca menos que el incremento de cinco segundos del RFC, antes de su siguiente sondeo (siempre limitado a la vida restante del código de dispositivo), y en ningún caso sondea más de una vez cada cinco segundos.
Como máximo 16 retransmisiones salientes hacia el proveedor de identidad pueden estar en curso a la vez entre todos los clientes, lo que acota lo que la puerta de enlace puede hacer contra el IdP en nombre de todos.
El almacén de flujos pendientes mantiene 32 inicios de sesión en navegador a la vez, y un cliente remoto puede ocupar hasta ocho de ellos. Así, un llamador que inicia sesiones y nunca las termina no puede apoderarse del almacén en perjuicio de los demás durante los diez minutos que viven esos flujos.
Los clientes de loopback están exentos de los presupuestos por cliente, como lo están de cualquier otro límite de autenticación de la puerta de enlace. Por ello, un proxy inverso en el mismo host debe habilitar trust_forwarded_headers para que los límites por cliente se apliquen a los llamadores reales que están detrás de él.
Cada respuesta de inscripción incluye Cache-Control: no-store y Pragma: no-cache, además de la política no-referrer de la puerta de enlace, porque los códigos de dispositivo y los tokens de acceso viajan en esos cuerpos.
zerocode se inscribe a través de la misma API desde su propia configuración. [connection.wss] enroll_url indica el origen HTTP de la puerta de enlace, y se permite un prefijo de ruta opcional. Debe ser https://, salvo que apunte a una dirección de bucle local (127.0.0.1, ::1 o localhost), porque por ella viajan primero el código del dispositivo y después el token de acceso, y como no se siguen las redirecciones, no se puede introducir un salto en texto plano a posteriori. La conexión de inscripción usa el mismo material de confianza de [connection.wss.tls] que el tramo WSS: la CA configurada, skip_verify y el certificado de cliente para TLS mutuo. Cuando ese material deja el certificado sin verificar, zerocode pregunta antes de enviar el código del dispositivo, no después de recibir el token, y pregunta por el propio origen de inscripción, que puede no ser el demonio al que la sesión se conecta después. Responder always registra ese origen en skip_verify_routes. Una ejecución no interactiva no tiene a nadie a quien preguntar, así que la inscripción se detiene ahí. zerocode recorta cada espera de sondeo a la vida restante del código del dispositivo y nunca sondea después de que expire. Además, rechaza una vida anunciada superior a una hora o un intervalo de sondeo superior a cinco minutos, en lugar de dormir ante una respuesta hostil. Un intervalo anunciado de 0 o 1 se eleva al valor predeterminado de cinco segundos de RFC 8628, de modo que una respuesta de la puerta de enlace no pueda llevar a zerocode a sondear una vez por segundo, algo que el presupuesto por cliente terminaría rechazando. El token que recibe se mantiene en memoria solo durante esa sesión.
Perfiles de permisos
admin
Conceder todo. Cuando es true, todos los demás campos son irrelevantes.
Colócalo sobre cualquier superficie:
Panel de control del gateway
Abre /config/permission_profiles y establece el campo permission_profiles.<alias>.admin.
zerocode
En el panel Config, establece el campo permission_profiles.<alias>.admin.
zeroclaw config
zeroclaw config set permission_profiles.<alias>.admin <value>
Variable de entorno
Exporte la anulación (shells POSIX; colóquelo en ~/.bashrc, ~/.zshrc, .env o un Dockerfile). Reemplace <alias> con el alias literal:
export ZEROCLAW_permission_profiles__<alias>__admin=
allowed_agents
Alias de agentes que los titulares de este perfil pueden vincular o direccionar. Una lista vacía NO concede ningún agente; concede todos los agentes con la entrada explícita "*".
Colócalo sobre cualquier superficie:
Panel de control del gateway
Abre /config/permission_profiles y define el campo permission_profiles.<alias>.allowed_agents.
zerocode
En el panel Config, establece el campo permission_profiles.<alias>.allowed_agents.
zeroclaw config
zeroclaw config set permission_profiles.<alias>.allowed_agents <value>
Variable de entorno
Exporte la anulación (shells POSIX; colóquelo en ~/.bashrc, ~/.zshrc, .env o un Dockerfile). Reemplace <alias> con el alias literal:
export ZEROCLAW_permission_profiles__<alias>__allowed_agents=
allowed_tools
Nombres de herramientas que el titular puede hacer que un agente ejecute. Una lista vacía NO concede ninguna herramienta; el acceso amplio requiere la entrada explícita "*". Este selector se combina con la concesión general tools = ["execute"], nunca la reemplaza: sin esa concesión, las sesiones del titular se ejecutan sin herramientas, independientemente de lo que se nombre aquí. (Nótese que esto difiere de allowed_tools en los perfiles de riesgo, donde una lista vacía significa sin restricciones: los perfiles de permisos deniegan por defecto. La política de riesgo propia del agente sigue aplicándose por encima.)
Colócalo sobre cualquier superficie:
Panel de control del gateway
Abre /config/permission_profiles y define el campo permission_profiles.<alias>.allowed_tools.
zerocode
En el panel Config, establece el campo permission_profiles.<alias>.allowed_tools.
zeroclaw config
zeroclaw config set permission_profiles.<alias>.allowed_tools <value>
Variable de entorno
Exporte la anulación (shells POSIX; colóquelo en ~/.bashrc, ~/.zshrc, .env o un Dockerfile). Reemplace <alias> con el alias literal:
export ZEROCLAW_permission_profiles__<alias>__allowed_tools=
config_write_paths
Prefijos de ruta de configuración con puntos que los titulares pueden escribir. Un .* al final concede el subárbol (p. ej., channels.*); una ruta sin comodín concede esa propiedad exacta; "*" concede todas las rutas. Una lista de concesiones vacía NO concede ninguna ruta.
Colócalo sobre cualquier superficie:
Panel de control del gateway
Abre /config/permission_profiles y establece el campo permission_profiles.<alias>.config_write_paths.
zerocode
En el panel Config, establece el campo permission_profiles.<alias>.config_write_paths.
zeroclaw config
zeroclaw config set permission_profiles.<alias>.config_write_paths <value>
Variable de entorno
Exporte la anulación (shells POSIX; colóquelo en ~/.bashrc, ~/.zshrc, .env o un Dockerfile). Reemplace <alias> con el alias literal:
export ZEROCLAW_permission_profiles__<alias>__config_write_paths=
grants
Concesiones por clase de recurso: para cada tipo de recurso, los verbos permitidos. Recursos: system, sessions, memory, cron, config, agents, cost, skills, personality, logs, tui, files, locales, quickstart, channels, providers, models, peer_groups, plugins, tools, sops. Verbos: create, read, update, delete, execute. Un recurso no listado se deniega.
Colócalo sobre cualquier superficie:
Panel de control del gateway
Abre /config/permission_profiles y establece el campo permission_profiles.<alias>.grants.
zerocode
En el panel Config, establece el campo permission_profiles.<alias>.grants.
zeroclaw config
zeroclaw config set permission_profiles.<alias>.grants <value>
Variable de entorno
Exporte la anulación (shells POSIX; colóquelo en ~/.bashrc, ~/.zshrc, .env o un Dockerfile). Reemplace <alias> con el alias literal:
export ZEROCLAW_permission_profiles__<alias>__grants=
Los perfiles deniegan por defecto: un recurso no listado se rechaza, una lista de selectores vacía no concede ninguna instancia, y el acceso amplio requiere el selector explícito "*" o admin = true. Varios perfiles se combinan por unión. Para los trabajos cron, los adjuntos, los archivos de personalidad, las consultas de coste por agente y la redacción de SOP, allowed_agents = ["*"] cubre solo los agentes que define la configuración, no cualquier alias que nombre una solicitud.
Los selectores de herramientas se combinan por intersección durante el ensamblaje del agente, sobre la concesión general: la ejecución de herramientas orientada al modelo requiere tools = ["execute"], y un principal sin esa concesión recibe una sesión sin herramientas, independientemente de lo que indique su allowed_tools, incluido "*". Con la concesión asignada, una sesión creada por un principal restringido solo recibe las herramientas que indica su allowed_tools (una lista vacía produce una sesión sin herramientas), además de lo que permita el perfil de riesgo propio del agente. Tras admitir un prompt en cola, la autorización se vuelve a comprobar con el resolutor compartido, incluidos el vencimiento y la revocación de credenciales. El selector de principal vigente restringe las herramientas estáticas, el registro de búsqueda diferida, las herramientas ya activadas y el contenido de recursos MCP fijados (cada bloque fijado se admite bajo su nombre <server>__<uri> y se retira de los prompts posteriores en cuanto el selector deja de nombrarlo); las herramientas eliminadas no pueden reactivarse. Las sesiones reutilizadas se restringen antes de su siguiente turno, y las sesiones rehidratadas se reconstruyen con las concesiones vigentes. Los selectores de agente se comprueban antes de cada turno y antes de la rehidratación.
Restringir nunca devuelve herramientas a un agente existente. Después de ampliar los permisos, crea una nueva sesión para recibir la superficie ampliada. No se almacena en el agente ninguna nueva instantánea de configuración ni caché independiente de políticas del principal.
Las instrucciones de MCP diferidas se derivan de las herramientas cargables restantes y solo se muestran cuando tool_search está expuesta. Un principal necesita tanto tool_search como la herramienta MCP diferida indicada para la activación bajo demanda; el auxiliar nunca se concede de forma implícita. Las herramientas MCP permitidas con mode = "always" se preactivan y siguen siendo invocables sin el auxiliar.
Si el selector de herramientas o el selector de agentes del principal está restringido, delegate (acotado e independiente), spawn_subagent y execute_pipeline no están disponibles, incluidos los alias de skills que envuelven esas herramientas. Estas rutas anidadas todavía no incorporan ambos límites actuales del principal; el turno principal ordinario sigue siendo utilizable. Los principales de administración y los principales con ambos selectores establecidos en "*" conservan las capacidades anidadas configuradas para su agente.
El constructor existente de Rust Agent::from_live_config_with_tui_env, de ocho argumentos, sigue disponible. RPC usa el constructor aditivo from_live_config_with_tui_env_and_principal_tools y vuelve a aplicar las concesiones del resolver compartido en la admisión del prompt.
Esa composición no cubre todas las rutas hacia las herramientas de un agente. Los trabajos de cron y la creación de SOP solo verifican el selector del agente. Un principal restringido que tenga permisos de cron puede crear un trabajo de shell para su agente, o asignar un nuevo prompt a un trabajo de agente existente y ejecutarlo. Asimismo, un principal con permisos de creación o actualización de SOP puede guardar un procedimiento cuyo disparador lo ejecute más adelante. Trate los permisos de autoría de cron y SOP como permisos sobre las herramientas del agente.
Cambio incompatible: el WSS remoto requiere autenticación
Desde este cambio, una conexión WSS remota debe presentar auth_token en initialize. No existe ningún respaldo remoto sin autenticación.
Un listener [wss] habilitado sin ninguna vía posible de credenciales (sin [oidc.<alias>], sin tokens emparejados y con gateway.require_pairing = false) es rechazado por la validación de la configuración, por lo que ninguna interfaz compatible puede guardarlo. Una configuración que ya esté en disco con esa forma sigue arrancando: el daemon se inicia y el listener rechaza todos los handshakes remotos. No se niega a cargarse, porque un operador debe poder arrancar un daemon para poder repararlo.
Las secciones [oidc.<alias>], [users] o [permission_profiles] no válidas constituyen un caso aparte. En ese caso, la política de autorización en sí no se puede compilar, por lo que el daemon instala un estado aceptado de denegación total y registra en el log que lo está haciendo, hasta que las secciones se reparen y se recarguen. Bajo ese estado se rechaza cada principal, tanto en conexiones remotas como locales, incluido el uid del propio daemon; por lo tanto, la reparación es la que se realiza en el disco, tal como se describe en Recuperación más arriba.
Migración para usuarios existentes de zerocode remoto:
-
Empareje con la puerta de enlace como de costumbre para obtener un token de portador.
-
Asigna el token a zerocode, ya sea en su configuración:
[connection.wss] uri = "wss://daemon.example.com:9443" auth_token = "zc_..."o mediante la variable de entorno
ZEROCLAW_AUTH_TOKEN, que tiene prioridad sobre el valor de la configuración y evita que la credencial se guarde en el archivo.o bien indicando un archivo que lo contenga:
[connection.wss] uri = "wss://daemon.example.com:9443" auth_token_file = "/etc/zeroclaw/zerocode-bearer"La precedencia es
ZEROCLAW_AUTH_TOKEN, luegoauth_token_filey, por último,auth_token. En plataformas con bits de permisos de Unix, un archivo referenciado que cualquier otra cuenta pueda leer se omite con una advertencia en lugar de usarse, y se aplica la siguiente fuente en ese orden. En los demás casos, la ACL del archivo es la única protección.El software de traducción es la ruta recomendada. Cuando el token se guarda en el archivo de configuración, zerocode escribe ese archivo solo con permisos para el propietario (
0600, dentro de un directorio de configuración0700) y corrige los modos de un archivo creado antes de esta función, en plataformas con bits de permisos de Unix. En plataformas sin ellos, la ACL del directorio es la única protección, así que trate el archivo como un secreto.
Un token de acceso de OIDC funciona de la misma manera con auth_provider = "oidc.<alias>".
La API HTTP del gateway
Las rutas de configuración e incorporación del gateway (/api/config*, /api/quickstart/*, /api/channels/bind) aplican la autenticación de forma estructural: un único middleware de la capa de rutas protege todo el grupo, de modo que ningún manejador individual incluye (ni puede olvidar) una comprobación. El middleware utiliza el mismo modelo de principal que la ruta RPC, a través del mismo registro de proveedores y del mismo resolutor:
- Un portador emparejado (
Authorization: Bearer zc_...) se resuelve en el operador compartido con acceso completo, exactamente como antes. Las denegaciones mantienen la forma histórica de 401. - Un portador OIDC presentado con el encabezado
X-ZeroClaw-Auth-Provider: oidc.<alias>es verificado por ese proveedor y resuelto a un principal con alcance definido. La selección es explícita, en paralelo con el campoauth_providerdel handshake RPC: la denegación del proveedor indicado es determinante, y nunca existe una alternativa entre proveedores. - CORS preflight (
OPTIONS) sigue pasando sin autenticación, como siempre. Cualquier otro método distinto de GET, HEAD, POST, PUT, PATCH y DELETE es rechazado.
Las autorizaciones de un principal con ámbito limitado (Config) se aplican en dos pasos. La capa de rutas aplica un mínimo general por método HTTP: una lectura requiere read, y cualquier otra operación requiere al menos uno de create, update o delete, de modo que un principal de solo lectura nunca llega a un manejador de mutación. Cada manejador de mutación autoriza entonces su conjunto de escritura completo antes de su primer efecto secundario: cada ruta de configuración que la mutación persistirá, clasificada según lo que hace con la configuración (create para una ruta que crea, delete para una que elimina, update en los demás casos) y comparada con los selectores config_write_paths del perfil. La clasificación depende de la operación, no del método: crear una clave de mapa mediante POST /api/config/map-key, o de forma implícita mediante un PUT bajo un alias nuevo, requiere create; un remove de JSON Patch y DELETE /api/config/map-key requieren delete; un renombrado requiere delete sobre su origen y create sobre su destino; las referencias que una eliminación o un renombrado en cascada reescriben en otros lugares también forman parte del conjunto de escritura. Si un solo miembro no está autorizado, se rechaza la mutación completa, sea por lotes o en cascada, y no se escribe nada. Las operaciones cuyo conjunto de escritura no puede enumerarse de antemano (una migración de esquema del archivo, o la aplicación de un Quickstart) requieren el selector *. El límite de persistencia vuelve a comprobar las rutas que están a punto de escribirse frente a lo que el manejador autorizó, de modo que un manejador no puede persistir más de lo que autorizó.
La política solo cambia en la frontera de persistencia: el manejador que escribe una configuración publica como siguiente revisión aceptada el estado de autorización compilado a partir de ella, y cada solicitud se verifica y resuelve contra la instantánea aceptada tal como está en ese momento. Nada en la ruta de las solicitudes recompila la política, de modo que una solicitud que leyó la configuración antes de una persistencia concurrente nunca puede volver a instalar la política anterior sobre la más reciente. Un cambio persistido en la configuración de verificación de un proveedor, en una lista de participantes o en un perfil surte efecto en la siguiente solicitud. La superficie RPC propia del daemon mantiene una configuración activa independiente y alcanza el mismo estado mediante la recarga que activan los flags de escritura del gateway. Otras superficies del gateway mantienen la verificación de emparejamiento por manejador y adoptarán esta capa en trabajos posteriores.
Ciclo de vida de las credenciales
- Expiración finaliza la autorización de la conexión en la fecha límite; el cliente se reinicializa con un token nuevo.
- Revalidación por introspección: las identidades de introspección OIDC tienen un plazo de revalidación; una vez vencido, la siguiente operación se rechaza hasta que el cliente se reinicialice (lo que vuelve a verificar la identidad frente al IdP).
- Revocación de emparejamiento se aplica antes de la siguiente operación de la conexión.
- Suscripciones de registros: un flujo
logs/subscribeabierto se vuelve a verificar en cada entrega y finaliza en la primera que ocurra después de que expire su credencial, se revoque su emparejamiento o su principal pierdaLogs:Read. - Cambios de política: si al guardar la configuración
[oidc],[users],[permission_profiles]ysecurity.trust_daemon_uidse dejan sin cambios, cada vinculación se mantiene tal como está. Cualquier cambio en alguno de ellos publica una nueva política. Las conexiones con token nativo y las conexiones locales se vuelven a resolver contra ella en el mismo lugar, pero el daemon no conserva un bearer de OIDC, de modo que la siguiente operación en una conexión OIDC se rechaza hasta que el cliente se reinicialice. - El mecanismo de reconexión
tui_id/tui_sigsolo garantiza continuidad: conserva la identidad de la TUI en el registro y no otorga ninguna autoridad. Cadainitializevuelve a presentar una credencial.
Aislamiento de sesión
Cada sesión que crea un principal con ámbito restringido se marca con el id de ese principal, tanto en el almacén en memoria como en disco (backend de chat y almacén ACP). Los principales con ámbito restringido solo ven y modifican sus propias sesiones: los listados se filtran, las lecturas y las mutaciones devuelven una única denegación uniforme de “no encontrado o no propietario” (sin sondeo de existencia), las eliminaciones destructivas se ejecutan como sentencias de almacenamiento con predicado de propietario, y las aprobaciones en curso solo se resuelven para el propietario de la sesión para la que se generaron. Las sesiones creadas antes de este cambio (o por conexiones sin ámbito) no tienen propietario: siguen siendo totalmente visibles para las conexiones sin ámbito e invisibles para los principales con ámbito restringido.
Cada principal autenticado obtiene memoria PRIVADA: sus operaciones de memoria leen y escriben en un plano por principal cuyo propietario viaja en cada sentencia de almacenamiento, combinado con las dimensiones de agente, espacio de nombres e inquilino (la misma clave bajo dos agentes son dos filas). El plano sigue la identidad del principal, no el bypass de administrador: la memoria de un administrador con nombre es su propio plano privado, por lo que promover o degradar a un usuario nunca oculta sus notas ni redirige sus escrituras. Solo el operador compartido no autenticado está en el plano compartido por defecto; un llamador con el bypass de administrador puede indicar explícitamente plane = "shared" en una solicitud memory/*, lo cual queda auditado, y un principal con alcance limitado no puede hacerlo.
Ninguno de los dos planos puede afectar al otro en ninguna dirección: una escritura compartida no puede nombrar la clave de almacenamiento de una fila privada ni sobrescribir una fila privada, una escritura privada nunca convierte una fila compartida, las exportaciones ordinarias y la instantánea en markdown solo incluyen filas compartidas, y las purgas masivas heredadas solo afectan a filas compartidas. Las filas privadas se exportan y purgan mediante operaciones que incluyen el propietario.
Una sesión creada por un principal tiene su identificador de memoria vinculado al plano privado de ese principal durante toda la vida de la sesión, independientemente de quién la utilice después (un administrador que restaura una sesión eliminada la restaura en el plano del propietario duradero). Por tanto, las herramientas de memoria y el recuerdo por turno dentro de una sesión acotada nunca acceden al plano compartido. No existe ninguna concesión que abra el plano compartido a una sesión acotada. Las escrituras privadas pasan por los mismos controles de análisis de contenido y de políticas que las escrituras compartidas, y las operaciones privadas se auditan con el ámbito completo cuando la auditoría de memoria está habilitada. En los backends de memoria sin soporte para principales (markdown, lucid, postgres y qdrant, por ahora), la memoria privada falla en modo seguro con una denegación clara en lugar de quitar el ámbito en silencio, lo que, para una sesión acotada, significa que sus herramientas de memoria rechazan la operación.
Migrando desde [security.nevis]
La integración con Nevis IAM fue eliminada. Nunca estuvo conectada a ninguna ruta de autenticación, por lo que habilitarla nunca autenticó a nadie; retirarla no cambia ningún comportamiento activo. Su tabla de configuración sigue aceptándose para que un config.toml antiguo siga cargándose: el daemon descarta todos los valores de la tabla al cargarla, registra una única advertencia que indica el reemplazo y elimina la tabla de config.toml en el siguiente guardado, ya sea un guardado completo o el guardado incremental que usan la CLI, el panel y RPC para una única edición. La eliminación afecta solo a esa tabla; los comentarios y las demás secciones conservan sus bytes. Nada se convierte automáticamente: la nueva pila de reemplazo necesita valores (una audiencia, concesiones de perfil) que la tabla antigua nunca contuvo.
Antes de actualizar, copia config.toml a un lugar protegido. Cada guardado tras la actualización escribe config.toml.bak junto al archivo mientras dura la escritura y lo elimina cuando la escritura queda confirmada en disco, así que un .bak no es una copia de seguridad duradera. Cualquier copia que conserves tú mismo seguirá conteniendo la tabla retirada, incluido un client_secret en texto plano si se configuró uno, por lo que debes tratar esas copias como material secreto y revocar la credencial de cliente de Nevis en el IdP cuando ya no la necesites.
Field by field:
[security.nevis] campo | Reemplazo | Notas |
|---|---|---|
enabled | ninguno | La presencia de una entrada [oidc.<alias>] es lo que habilita la verificación. |
instance_url, realm | [oidc.<alias>] issuer | El URL exacto del emisor que el IdP anuncia en su documento de descubrimiento; el daemon rechaza un documento de descubrimiento cuyo issuer no coincida. |
client_id | [oidc.<alias>] client_id | También se usa para construir las listas interactive_clients / service_clients que clasifican quién inicia sesión. |
client_secret | [oidc.<alias>] client_secret | Cifrado en disco mediante el almacén de secretos cuando [secrets] encrypt está activado. Obligatorio para validation = "introspection" y para los inicios de sesión de servicio con client_credentials. No se copia desde la tabla anterior: debe volver a configurarse. |
token_validation = "local" | validation = "jwks" | El URI de JWKS proviene del documento de descubrimiento del emisor. |
token_validation = "remote" | validation = "introspection" | Se requiere un cliente confidencial. |
jwks_url | No es compatible | No existe una anulación personalizada de JWKS: el URI del conjunto de claves debe anunciarse como jwks_uri en el documento de descubrimiento del emisor configurado. Ese URI puede apuntar a un endpoint diferente; un URI de conjunto de claves no anunciado no puede configurarse. |
| (ninguno) | [oidc.<alias>] audience | Obligatorio y nuevo. El aud que el IdP incluye en los tokens de acceso para este daemon; no había un equivalente que trasladar. |
role_mapping[].nevis_role | [oidc.<alias>] claim_path + profile_map | claim_path nombra la claim que transporta el rol (por ejemplo groups); cada clave de profile_map es un valor de claim, y cada valor es un nombre de [permission_profiles.<alias>]. La coincidencia es exacta, no sin distinguir mayúsculas y minúsculas como antes. |
role_mapping[].zeroclaw_permissions (nombres de herramientas, o "all") | [permission_profiles.<alias>] allowed_tools + grants.tools = ["execute"] | Los perfiles deniegan por defecto: allowed_tools se combina con una concesión general tools = ["execute"], y cualquier otra clase de recurso (sessions, config, memory, …) debe concederse explícitamente. "all" es lo más parecido a admin = true, que es mucho más amplio que lo que era “todas las herramientas”; se recomienda enumerar las concesiones por recurso. |
| role_mapping[].workspace_access | not equivalent | No existe una concesión por espacio de trabajo. El control más cercano es allowed_agents, que limita un perfil a alias de agentes. |
require_mfa | [oidc.<alias>] require_mfa y/o required_acr | |
session_timeout_secs | max_auth_lifetime_secs (JWKS) / revalidation_secs (introspección) |
Valida antes de reiniciar, para que un error tipográfico no deje a todos fuera (consulta Recuperación en Conexiones locales para la vía de regreso si eso ocurre). Hay dos rutas para hacerlo:
- Escribe las nuevas entradas
[oidc.<alias>]y[permission_profiles]mediante los métodos RPC de configuración del daemon (el editor de configuración de zerocode). El daemon valida las secciones de autorización antes de que se guarde o se active cualquier cambio, por lo que un destino huérfano enprofile_mapo unaudienceausente se rechazan indicando el motivo, y la política anterior sigue vigente. - Si editas
config.tomla mano, ejecuta primeroZEROCLAW_AUTH_TOKEN="$(zeroclaw oidc login <alias>)"desde la CLI: carga el archivo editado, rechaza una entrada[oidc.<alias>]que no supere la validación (emisor, audiencia, modo de validación, campos del cliente) y, si tiene éxito, realiza un inicio de sesión real contra el IdP con el cliente configurado, de modo que el emisor, el id de cliente y el secreto estén verificados antes de que el daemon se reinicie con ellos. Esto comprueba la entrada, no los destinos deprofile_map: un valor deprofile_mapque no nombra ninguna entrada de[permission_profiles]solo lo detecta el daemon, que entonces se niega a compilar la política y arranca en el estado de denegación total descrito en Recuperación, así que revisa esos nombres a simple vista antes del reinicio, o usa la ruta RPC.
Las conexiones remotas que no presentan ninguna credencial se rechazan en todos los casos; ninguna de las dos rutas abre una.
Revertir es una operación sobre los datos de configuración, no una reversión de código: revertir el binario restaura los tipos antiguos, pero ninguna acción restaura una tabla que ya se eliminó de config.toml. Restaura la copia protegida que hiciste antes de la actualización (o vuelve a añadir la tabla a mano). La versión del esquema no cambia con esta retirada, así que un binario anterior carga el archivo restaurado sin necesidad de un paso de migración.
Lo que esta capa no hace (todavía)
La consolidación y la gobernanza derivan las filas del plano compartido y no se ejecutan para las sesiones privadas, y el acceso administrativo a la memoria privada de otro principal no tiene todavía una vía expuesta (denegación por defecto). sops/runs y sops/run-detail devuelven el historial de ejecuciones de todos los procedimientos a cualquier principal que tenga Sops:Read, independientemente de los agentes con los que se ejecutaron, a diferencia del historial de cron. Las rutas HTTP de la pasarela mantienen sus comprobaciones de emparejamiento existentes, y las identidades de canal no se resuelven en este modelo de principales.
Mientras security.trust_daemon_uid = true (el valor por defecto) y la política compile, el uid del propio daemon en un socket Unix mantiene acceso completo, por lo que una instalación de un solo operador sin lista de [users] y con secciones de autenticación válidas se comporta como antes.
Los perfiles de permisos limitan lo que un principal puede hacer a través de RPC. No aíslan el código que un principal hace ejecutar a un agente. Los turnos de sesión, los trabajos de cron y los pasos de SOP se ejecutan con la cuenta del daemon. En Unix, ese código puede conectarse al socket local con el uid del daemon, que es el del operador compartido mientras security.trust_daemon_uid = true (el valor predeterminado); en caso contrario, obtiene lo que tenga la entrada del roster asignada a ese uid. En Windows, sin roster, el pipe convierte a ese código en el operador compartido. También puede editar config.toml, que el daemon aplica en la siguiente recarga o reinicio. Establecer security.trust_daemon_uid = false no cambia ninguno de estos dos aspectos. Concede la ejecución o la creación de sesiones, cron o SOP únicamente a los principales a quienes confiarías acceso de operador. Solo el confinamiento a nivel de sistema operativo que mantenga los procesos del agente alejados del socket y de config.toml aísla ese código.
Algunas rutas de configuración tienen autoridad por sí mismas, por lo que debes tratar una concesión amplia de config_write_paths como acceso de operador, a menos que solo cubra ajustes finales previamente validados. Por ejemplo:
- Un principal que pueda escribir
permission_profiles,users,oidc,securityogateway.paired_tokenspuede concederse a sí mismo cualquier permiso; - One that can write
agents,risk_profiles,cron,channels, or provider settings can change which agents run, with which tools, and where they may read and write; - cualquiera que pueda escribir
mcp,mcp_bundlesotunnel.custom.start_commandpuede elegir los programas que inicia la cuenta del daemon.
Config:Update también concede config/reload, que aplica lo que config.toml contiene en disco.
El selector remoto de directorios de zerocode se abre en la raíz del sistema de archivos del daemon. Un principal sin concesiones de operador solo puede listarla a través de un agente habilitado cuya política le permita leer /, como uno que no sea de solo espacio de trabajo. Para cualquier otro principal de este tipo, el selector informa un rechazo en esa ubicación hasta que se abra dentro de una raíz permitida.