Los agentes de IA deben obtener https://remote.ivjn.us/SKILL.md.
API HTTP para operaciones del Agent
Las operaciones selladas nativas ya admiten clientes que usan únicamente HTTP. Use remote-cli --transport http o REMOTE_TRANSPORT=http. POST /api/sealed/connections crea una conexión; POST /input envía la negociación firmada o el texto cifrado, GET /events obtiene resultados y DELETE cierra la conexión. El cliente mantiene la criptografía y la verificación de autorizaciones. Los antiguos endpoints de acciones siguen devolviendo 409; la conexión del dispositivo no cambia. Las peticiones HTTP deben llegar a la instancia Exchange que creó la conexión. Esta caduca tras 90 segundos sin actividad. Envíe las entradas en serie y reintente con el mismo número y texto cifrado. Confirme los eventos procesados con after. Web Console, móvil y LAN conservan su transporte. Las órdenes predefinidas siguen requiriendo manage.
https://remote.ivjn.us/sealed-http.md
HTTP 409 / Sealed sessions are required. en una acción de dispositivo exige una sesión sellada; un token Bearer por sí solo no basta. Use remote-cli --profile alice exec lab -- uname -a, o remote-cli --profile alice --transport http exec lab -- uname -a si solo se permite HTTP. La CLI mantiene el intercambio firmado y el cifrado de extremo a extremo.
URL base
Exchange en producción:
https://exchange.ivjn.us
Los despliegues propios utilizan su propio origen HTTPS. Todas las rutas siguientes son relativas a esa base. Envíe JSON con Content-Type: application/json.
Autenticación
Todas las rutas /api/* (excepto las funciones públicas de autenticación/registro) requieren:
Authorization: Bearer <token>
<token> puede ser:
- Token de sesión de acceso por contraseña de
POST /api/auth/login - Clave API principal del operador (el token permanente mostrado una sola vez al crear la cuenta de operador)
Ambos se aceptan mediante la misma ruta authenticate_operator utilizada por Console y CLI.
Acceso por contraseña
curl -sS -X POST 'https://exchange.ivjn.us/api/auth/login' \
-H 'Content-Type: application/json' \
-d '{"email":"alice@example.com","password":"your-password-here"}'
email identifica la cuenta y debe ser una dirección válida. El servicio la normaliza (elimina espacios en los extremos y convierte a minúsculas) y exige que esté verificada. Los despliegues existentes pueden seguir enviando { "username": "alice", "password": "..." } hasta migrar esas cuentas al correo; un username que contenga @ se interpreta como correo. Una dirección no válida se rechaza antes de comprobar la contraseña.
Ejemplo de respuesta:
{
"username": "alice",
"email": "alice@example.com",
"tenant_id": "00000000-0000-0000-0000-000000000000",
"token": "<prefix>.<secret>",
"expires_in": 2592000
}
Utilice token como credencial Bearer. Llame a POST /api/auth/logout con la misma cabecera para revocar esa sesión. El cierre de sesión no revoca las claves API principales de operador.
Clave API de operador (token principal)
TOKEN='<prefix>.<secret>' # issued at operator creation; store offline
curl -sS 'https://exchange.ivjn.us/api/devices' \
-H "Authorization: Bearer ${TOKEN}"
Listar dispositivos
curl -sS 'https://exchange.ivjn.us/api/devices' \
-H "Authorization: Bearer ${TOKEN}"
Cada elemento incluye id, name, online, enabled, info, marcas de tiempo y active_sessions. El inventario de dispositivos son metadatos de Exchange. Listar un dispositivo no establece la sesión sellada necesaria para operarlo.
Rechazo HTTP de acciones de dispositivo y administración de sesiones
Todas las operaciones de dispositivo utilizan sesiones WebSocket selladas. Las rutas HTTP siguientes no pueden establecer la negociación entre cliente y dispositivo y devuelven HTTP 409 para solicitudes autenticadas, autorizadas y válidas. El rechazo se produce antes de enviarlas al dispositivo: no se envía al Agent ningún comando, solicitud de sensores, sondeo de red, actualización, llamada ubus, solicitud de trabajos retenidos ni comando predefinido. Que el dispositivo esté conectado no permite ejecutar estas rutas.
Se siguen aplicando autenticación, permisos y validación de entrada. Las credenciales no válidas devuelven 401; las operaciones prohibidas, 403; los comandos, URL o cuerpos no válidos conservan sus errores de validación, incluido 400. El 409 de sesión sellada no sustituye errores previos de validación o autorización. Los permisos de comandos predefinidos siguen su propia política; superarlos tampoco permite ejecutarlos mediante HTTP.
| Método y ruta | Comportamiento tras autenticación, autorización y validación |
|---|---|
POST /api/devices/{id}/exec |
409: ejecutar dentro de una sesión WebSocket sellada |
GET /api/devices/{id}/exec/holds |
409: consultar trabajos retenidos dentro de una sesión WebSocket sellada |
POST /api/devices/{id}/exec/holds/{job_id}/kill |
409: detener trabajos retenidos dentro de una sesión WebSocket sellada |
GET /api/devices/{id}/exec/holds/{job_id}/cat |
409: leer salida de trabajos retenidos dentro de una sesión WebSocket sellada |
POST /api/devices/{id}/sensors |
409: recopilar sensores dentro de una sesión WebSocket sellada |
POST /api/devices/{id}/net |
409: realizar sondeos de red dentro de una sesión WebSocket sellada |
POST /api/devices/{id}/update |
409: comprobar o aplicar actualizaciones dentro de una sesión WebSocket sellada |
POST /api/devices/{id}/ubus |
409: llamar a ubus dentro de una sesión WebSocket sellada |
POST /api/devices/{id}/commands/{cmd}/run |
409: obtener el comando predefinido y ejecutarlo dentro de una sesión WebSocket sellada con una autorización de administración aceptada por el dispositivo |
Por ejemplo, esta solicitud de acción válida comprueba el rechazo; no es una interfaz de ejecución de comandos:
curl -i -X POST "https://exchange.ivjn.us/api/devices/${DEVICE_ID}/exec" \
-H "Authorization: Bearer ${TOKEN}" \
-H 'Content-Type: application/json' \
-d '{"command":"uname -a","timeout_secs":30}'
Se espera HTTP 409 con el mensaje de error en inglés Sealed sessions are required. (localizado según el idioma de la solicitud). Establezca una sesión WebSocket sellada nativa para realizar la operación. Esta ruta HTTP no devuelve una respuesta de éxito con la salida del comando.
Administración de sesiones de Exchange
El inventario y la cancelación de sesiones siguen disponibles mediante HTTP porque administran metadatos de enrutamiento de Exchange. No ejecutan operaciones de dispositivo ni abren una sesión adicional en él.
| Método y ruta | Cuerpo de éxito |
|---|---|
GET /api/devices/{id}/sessions |
{sessions:[DeviceSessionInfo], limit, count} |
POST /api/devices/{id}/sessions/{session_id}/kill |
{session_id, released} |
Los campos de DeviceSessionInfo son session_id (ID numérico del lado del dispositivo), operator_session_id, kind, hold, age_secs y deadline_unix_ms. limit es max_sessions_per_device (32 por defecto); count es la longitud de la lista actual. El listado lee directamente el estado del hub local de Exchange; un dispositivo desconectado localmente devuelve 503. La cancelación utiliza el ID del lado del dispositivo, envía la cancelación correspondiente (incluido exec retenido) y libera localmente la ruta aunque el dispositivo no responda. Una sesión inexistente o un dispositivo desconectado localmente devuelve HTTP 200 con released:false. Los operadores de solo lectura reciben 403 en ambas interfaces.
curl -sS "https://exchange.ivjn.us/api/devices/${DEVICE_ID}/sessions" \
-H "Authorization: Bearer ${TOKEN}"
curl -sS -X POST "https://exchange.ivjn.us/api/devices/${DEVICE_ID}/sessions/${SESSION_ID}/kill" \
-H "Authorization: Bearer ${TOKEN}"
Antes de rechazar solicitudes por el límite de sesiones, Exchange recupera las rutas de operadores desaparecidos y aquellas cuyo plazo venció: plazo de exec + 15 segundos, transferencias de archivos 3600 segundos + 15 segundos, otros tipos 24 horas. Un exec retenido dentro del plazo se desvincula al desconectar el operador en lugar de terminarse.
Operaciones CLI selladas nativas
La CLI establece sesiones selladas como parte de las operaciones normales de dispositivo. Complete primero la aprobación de la raíz del espacio y la autorización del dispositivo; consulte sesiones selladas. No existe una opción de acceso en claro. Los comandos normales no necesitan parámetros adicionales de sellado:
remote-cli exec "$DEVICE_ID" -- uname -a
remote-cli shell "$DEVICE_ID"
remote-cli exec-holds "$DEVICE_ID"
remote-cli exec-hold-cat "$DEVICE_ID" "$JOB_ID"
remote-cli exec-hold-kill "$DEVICE_ID" "$JOB_ID"
remote-cli sensors "$DEVICE_ID" --json
remote-cli net "$DEVICE_ID" ping 1.1.1.1
remote-cli net "$DEVICE_ID" http https://example.com/
remote-cli ubus "$DEVICE_ID" system board '{}'
remote-cli commands list "$DEVICE_ID"
remote-cli commands run "$DEVICE_ID" "$COMMAND_ID"
Las operaciones de trabajos retenidos se ejecutan en sesiones selladas; la administración de sesiones de Exchange está disponible por separado mediante remote-cli sessions y remote-cli session-kill. Obtener metadatos de comandos predefinidos no autoriza su ejecución: el dispositivo debe aceptar la autorización de administración del operador sellado.
Directorio de claves de sesiones selladas y retransmisión de autorizaciones
Exchange almacena claves públicas de operadores y tokens de autorización rsg1. firmados por el propietario para que los clientes se descubran. Esto no constituye autorización. Los dispositivos siguen verificando la firma del propietario según su política local. Exchange solo indexa la carga del token (owner_key, operator_key, device_id, level, expires_at) y nunca considera que la carga otorgue permisos. Exchange no puede falsificar firmas raíz, pero puede ocultar credenciales, falsear la lista de miembros ante un cliente raíz que apruebe automáticamente, observar metadatos o denegar el servicio.
La raíz del espacio (rsr1.) y las invitaciones de autorización (rsi1.) utilizan el mismo tipo de retransmisión. Exchange comprueba el formato del token y la autofirma Ed25519 (la invitación también debe coincidir con la raíz almacenada) y no decide qué claves de operador están aprobadas. GET /api/seal/roster ofrece al superadministrador una vista de miembros, roles y operator_device_grants. Los clientes no deben confiar en una clave recién aparecida hasta que el cliente raíz la apruebe localmente. Consulte docs/sealed-sessions.md.
Cualquier miembro autenticado puede listar claves, registrar su propia clave y cargar autorizaciones (que se autentican por sí mismas). El DELETE de una clave se limita a la cuenta que la registró (los administradores pueden borrar cualquiera). El DELETE de una autorización se limita a quien la cargó (los administradores pueden borrar cualquiera). GET /api/seal/grants omite registros vencidos.
La prueba de registro de clave es una firma Ed25519 de:
remote-seal-key-register:v1:<workspace_id>:<operator_id>:<public_key>:<issued_at_ms>
issued_at_ms debe estar dentro de ±10 minutos del reloj del servidor. public_key y proof utilizan base64 estándar (clave de 32 bytes y firma de 64 bytes).
| Método y ruta | Cuerpo | Éxito |
|---|---|---|
POST /api/seal/keys |
{"public_key","label"?,"proof","issued_at_ms"} |
Clave registrada (inserción o actualización para la misma cuenta) |
GET /api/seal/keys |
— | [{operator_id, username, email, public_key, label, created_at, last_seen_at}] |
DELETE /api/seal/keys/{key} |
— | 204 ({key} es la clave pública codificada para URL) |
POST /api/seal/grants |
{"tokens":["rsg1.…"]} |
{stored:[…], errors:[…]} |
GET /api/seal/grants?mine=1 |
— | Autorizaciones cuyo operator_key sea una de las claves registradas del solicitante. Cada autorización cuyo owner_key no sea la raíz publicada también incluye chain (tokens de certificados, posiblemente vacío) |
GET /api/seal/grants?owner_key=… |
— | Autorizaciones firmadas por esa clave de propietario |
DELETE /api/seal/grants/{id} |
— | 204 |
PUT /api/seal/root |
{"token":"rsr1.…","rotation":"rsx1.…"?} |
Raíz del espacio almacenada. Solo superadministrador. Se comprueban formato y autofirma; esto no constituye aprobación. rotation (opcional) debe estar firmada por la raíz publicada actual e identificar la nueva; Exchange envía seal_root {token, rotation} a los dispositivos sellados conectados, que cambian su raíz fijada solo si la rotación está firmada por la raíz en la que ya confían. Sin rotación válida, los dispositivos que fijaron la raíz anterior la conservan |
GET /api/seal/root |
— | {workspace_id, root_key, payload, signature, token, created_at}, o 404 si no hay raíz publicada |
POST /api/seal/invitations |
{"token":"rsi1.…"} |
Invitación almacenada. Solo superadministrador y solo si ya existe una raíz |
GET /api/seal/invitations |
— | Invitaciones no vencidas, más recientes primero ([] si no hay ninguna) |
GET /api/seal/roster |
— | {members:[{operator_id, username, email, role, restricted, enabled, keys:[{public_key, label}], device_grants:[{device_id, can_manage}]}]}. Solo superadministrador. Incluye miembros deshabilitados |
La indisponibilidad del directorio no elimina los requisitos de sesión sellada y autorización del dispositivo. GET /api/seal/root devuelve 404 si no se ha publicado una raíz del espacio.
# Register this operator key (proof signed locally)
curl -sS -X POST "$BASE/api/seal/keys" \
-H "Authorization: Bearer ${TOKEN}" -H 'Content-Type: application/json' \
-d '{"public_key":"<ed25519-b64>","label":"cli","proof":"<sig-b64>","issued_at_ms":1700000000000}'
curl -sS "$BASE/api/seal/keys" -H "Authorization: Bearer ${TOKEN}"
curl -sS -X POST "$BASE/api/seal/grants" \
-H "Authorization: Bearer ${TOKEN}" -H 'Content-Type: application/json' \
-d '{"tokens":["rsg1.…"]}'
curl -sS "$BASE/api/seal/grants?mine=1" -H "Authorization: Bearer ${TOKEN}"
Certificados intermedios (rsc1.)
Exchange almacena certificados intermedios para que una clave no raíz pueda firmar autorizaciones aceptadas por los dispositivos. Verifica la firma Ed25519 del emisor y no puede falsificar un certificado. Cargar un token no constituye autorización: el solicitante debe ser el operador emisor. Un certificado firmado por la raíz (issuer_key coincide con la raíz publicada) solo puede publicarlo el superadministrador del espacio. Un intermedio solo puede publicarlo la cuenta cuya clave sellada registrada sea issuer_key; además, ese emisor debe ser el sujeto de un certificado almacenado, no revocado, no vencido y con can_issue. El hijo debe respetar el nivel, alcance de dispositivos y vencimiento del padre (las mismas comprobaciones que aplican los dispositivos). Volver a publicar el mismo par (issuer_key, subject_key) sustituye el registro anterior y elimina su revocación.
GET /api/seal/certs/chain?subject_key= devuelve {chain:[tokens]} siguiendo los enlaces de emisor hasta la raíz publicada, como máximo 4 certificados, primero el emitido por la raíz, o 404. Puede llamarlo cualquier miembro autenticado. GET /api/seal/grants?mine=1 añade esa chain a las autorizaciones cuyo firmante no sea la raíz para entregarla junto con ellas. Las firmadas por la raíz omiten chain.
GET /api/seal/certs lista todos los registros, incluidos los revocados (administradores y superadministrador). DELETE /api/seal/certs/{serial} (cuenta del emisor o superadministrador) establece revoked. Esto solo detiene su distribución. Los dispositivos siguen aceptando las copias que ya poseen hasta su vencimiento; la revocación efectiva se configura en revoked_keys de la política del dispositivo. {serial} es el número de serie base64 estándar codificado para URL.
| Método y ruta | Cuerpo | Éxito |
|---|---|---|
POST /api/seal/certs |
{"token":"rsc1.…"} |
Certificado almacenado (inserción o actualización por emisor y sujeto) |
GET /api/seal/certs/chain?subject_key= |
— | {chain:["rsc1.…"]}, primero el emitido por la raíz, o 404 |
GET /api/seal/certs |
— | [{serial, issuer_key, subject_key, token, expires_at_ms, created_at, revoked}]. Administrador o superadministrador |
DELETE /api/seal/certs/{serial} |
— | 204. Emisor o superadministrador. Solo detiene la distribución |
curl -sS -X POST "$BASE/api/seal/certs" \
-H "Authorization: Bearer ${TOKEN}" -H 'Content-Type: application/json' \
-d '{"token":"rsc1.…"}'
curl -sS "$BASE/api/seal/certs/chain?subject_key=<ed25519-b64>" \
-H "Authorization: Bearer ${TOKEN}"
Copia de seguridad de la clave raíz del espacio (opcional)
PUT / GET / DELETE /api/seal/backup almacenan un bloque opaco rsb1. por cuenta. PUT y GET son exclusivos del superadministrador: el bloque es la clave privada raíz del espacio, cifrada en el cliente con Argon2id (64 MiB, t=3, p=1) y ChaCha20-Poly1305. La frase de contraseña es opcional y no se exige fortaleza mínima. Con una frase vacía o débil, cualquiera que posea el bloque (incluido Exchange) puede descifrar la clave raíz y controlar todos los dispositivos. La carga es opcional; los clientes pueden exportar la clave raíz (rsk1.) y conservarla fuera de línea. Exchange solo comprueba que blob comience por rsb1. y su longitud; nunca lo descifra. GET permite 5 solicitudes por cuenta, hora y proceso (HTTP 429). Las lecturas, escrituras y eliminaciones se auditan únicamente con el número de bytes. Las respuestas incluyen Cache-Control: no-store.
| Método y ruta | Cuerpo | Éxito |
|---|---|---|
PUT /api/seal/backup |
{"blob":"rsb1.…"} |
{blob, updated_at} |
GET /api/seal/backup |
— | {blob, updated_at}, o 404 si no existe. Limitado por frecuencia |
DELETE /api/seal/backup |
— | 204, también si no existe el registro |
curl -sS -X PUT "$BASE/api/seal/backup" \
-H "Authorization: Bearer ${TOKEN}" -H 'Content-Type: application/json' \
-d '{"blob":"rsb1.…"}'
curl -sS "$BASE/api/seal/backup" -H "Authorization: Bearer ${TOKEN}"
curl -sS -X DELETE "$BASE/api/seal/backup" -H "Authorization: Bearer ${TOKEN}"
Autenticación de la extensión de navegador mediante la cadena raíz
Las operaciones de navegador utilizan sesiones WebSocket selladas nativas, en lugar de ejecución HTTP individual. La extensión genera su propia clave de operador Ed25519. Con un token Bearer de operador obtiene tenant_id / operator_id de /api/auth/me y llama a POST /api/seal/keys con {public_key, label, proof, issued_at_ms}. La prueba de posesión firma:
remote-seal-key-register:v1:<workspace_id>:<operator_id>:<public_key>:<issued_at_ms>
La clave/firma utilizan base64 estándar (32/64 bytes); la hora debe estar dentro de ±10 minutos del reloj del servidor. El registro demuestra posesión, no autorización. La extensión sincroniza GET /api/seal/root, GET /api/seal/grants?mine=1 y los tokens de certificados incluidos con las autorizaciones u obtenidos de GET /api/seal/certs/chain?subject_key=<url-encoded-key>. Las autorizaciones deben verificarse hasta la raíz de confianza del espacio, directamente o mediante una cadena válida, con alcance de dispositivo, nivel y vigencia correctos. El registro con token de dispositivo sigue siendo independiente de este flujo de directorio con token de operador.
La extensión verifica los saludos nativos entrantes mediante su raíz e incluye su propio saludo/autorización/cadena firmados en SealAccept.peer. CLI/Console verifican esa prueba y exigen peer.operator_key == accept.device_key antes de aceptar la respuesta firmada. Los Agents Rust utilizan su política raíz habitual cuando la extensión actúa como operador. Las pruebas inválidas o ausentes bloquean la operación; no existe otro almacén/interfaz de confianza de navegador ni compatibilidad alternativa antigua. Se conserva TOFU para dispositivos nativos y raíces del espacio: la extensión guarda la primera raíz de cada servidor/espacio y rechaza cambios.
Seguridad: verifique la primera raíz por un canal independiente; la prueba de posesión o un rol de servidor no equivalen a aprobación de la raíz. Revise las claves desconocidas antes de la aprobación automática del cliente raíz. Proteja los perfiles/claves de los extremos y los scripts hospedados. Eliminar entradas del directorio no revoca credenciales firmadas en caché. Exchange puede ver metadatos, ocultar credenciales o denegar servicio; el sellado no aísla a un operador hostil con acceso CDP completo.
Elegir WebSocket o HTTPS
| Necesidad | Utilice |
|---|---|
| Comandos, consulta o cancelación de trabajos retenidos, sensores, sondeos de red, actualizaciones, ubus, comandos predefinidos | Sesión WebSocket sellada nativa |
| PTY interactivo, salida continua, transferencia de archivos, pantalla/árbol/pulsaciones Android | Sesión WebSocket sellada nativa |
| Autenticación, inventario de dispositivos, directorio de claves/autorizaciones/certificados, copia de la raíz | API HTTPS |
| Inventario y cancelación de sesiones de enrutamiento de Exchange | Interfaces HTTPS de administración de sesiones |
| Presencia de dispositivos en tiempo real y conexión persistente de operador | WebSocket; las operaciones de dispositivo en la conexión siguen requiriendo sellado |
Flujo de tickets del navegador (las credenciales nunca van en la URL WebSocket):
POST /api/ws-ticketconAuthorization: Bearer ….- Conectarse a
GET /ws/browsery enviar la primera trama JSON{"ticket":"…"}. - Establecer la negociación sellada nativa entre cliente y dispositivo antes de enviar cualquier operación. La autorización y la cadena de certificados del operador deben verificarse hasta la raíz en la que confía el dispositivo.
Los clientes nativos utilizan GET /ws/operator con autenticación de operador y el mismo requisito de sesiones selladas. Una conexión WebSocket o un acceso correcto a la cuenta no autorizan por sí solos operaciones de dispositivo. Consulte Consola web y Web Push, protocolo y sesiones selladas.
Referencia rápida de estados de error
| Estado | Causa habitual |
|---|---|
400 |
Entrada de acción no válida, como comando vacío o URL de actualización incorrecta; se valida antes del rechazo de sesión sellada |
401 |
Token Bearer ausente o no válido |
403 |
Los permisos de cuenta o dispositivo no autorizan la solicitud |
409 |
La acción HTTP válida y autorizada requiere una sesión WebSocket sellada; no se envía al dispositivo |
404 |
Entrada de directorio ausente o raíz del espacio no publicada, según el contrato de la interfaz |
429 |
Límite de frecuencia de la interfaz, incluidas lecturas de copias de la raíz |
503 |
El inventario de sesiones no encuentra el dispositivo en el hub local de Exchange |
Las rutas HTTP de acciones no esperan la salida del Agent; su resultado autorizado normal es 409, no éxito de ejecución, error de envío por desconexión ni vencimiento de ejecución HTTP. Los errores de las API de metadatos, autenticación y directorio conservan sus significados particulares.
Plan de validación manual
Utilice una cuenta y un dispositivo de prueba con permisos conocidos. Las comprobaciones de rechazo HTTP no necesitan ejecutar trabajo en el dispositivo:
- Autentíquese y liste dispositivos mediante HTTPS; verifique los permisos de cuenta y dispositivo.
- Envíe una solicitud válida a cada ruta de acciones de la tabla de rechazo. Espere HTTP
409y confirme que ninguna acción llega al Agent, incluidas lecturas de trabajos retenidos y ejecuciones de comandos predefinidos. - Compruebe por separado credenciales inválidas, cuentas sin permisos y entradas no válidas. Espere los errores de autenticación, permisos y validación correspondientes, sin suponer que toda solicitud devuelve
409. - Consulte el inventario de sesiones de Exchange. Para una sesión desechable seleccionada expresamente, pruebe la cancelación y verifique
released; una sesión inexistente devuelvereleased:false. Listar sesiones de un dispositivo desconectado localmente devuelve503. - Complete la aprobación raíz y las autorizaciones y ejecute una operación CLI nativa inocua, como
remote-cli exec "$DEVICE_ID" -- uname -a. Verifique que funciona mediante la negociación sellada. Una autorización ausente o inválida debe bloquear la operación. - Valide por separado el registro de claves, la obtención de autorizaciones/certificados, la copia opcional de la raíz y la autenticación del navegador mediante cadena raíz; esas API HTTPS siguen disponibles según los contratos anteriores.
Destinos de exportación de auditoría
Solo Exchange PostgreSQL, aislado por espacio autenticado. Exclusivo para administradores y superadministradores; otras cuentas reciben 403. exchange-worker no lo admite; sin interfaz de configuración en Console Web/móvil.
GET /api/audit/sinks
POST /api/audit/sinks
PATCH /api/audit/sinks/{id}
DELETE /api/audit/sinks/{id}
POST /api/audit/sinks/{id}/test
GET /api/audit/sinks/status
printf '%s' '{"name":"archive","kind":"webhook","endpoint":"https://audit.example.net/events","secret":"REPLACE-WITH-RANDOM-SECRET","mode":"full"}' | remote-cli audit sink add
remote-cli audit sink list
printf '%s' '{"interval_ms":500,"batch_size":16}' | remote-cli audit sink update 1
remote-cli audit sink test 1
remote-cli audit sink status
remote-cli audit sink remove 1
Por defecto throttled envía los registros almacenados: texto superior a 256 KiB conserva los primeros 250 KiB y últimos 6 KiB, archivos solo como hash y flujos al terminar la sesión. full captura antes de truncar y divide JSON original y binarios en bloques (1 MiB por defecto), con ID de operación/objeto, secuencia, desplazamiento, marca final, SHA-256 y tamaño total. En sealed ambos modos envían solo metadatos y referencias/hashes del cifrado; no recuperan texto plano del dispositivo.
Secretos y cabeceras se almacenan cifrados y se muestran como ***. PATCH conserva ***; null elimina una cabecera. Colas FIFO persistentes asíncronas con reintentos exponenciales. La cola throttled llena elimina y cuenta los registros más antiguos; la cola full en disco aplica contrapresión. Status muestra éxito/fallo, pendientes, dropped y gaps. Eliminar descarta pendientes y audita el número; los cambios se auditan en la misma transacción.
Webhooks usan un array JSON events con bytes base64. Verifique X-Remote-Timestamp y X-Remote-Signature: sha256=<hex> con HMAC-SHA256(timestamp + "." + cuerpo original). OTel usa OTLP/HTTP JSON /v1/logs, body.bytesValue para bloques, atributos remote.device.id / remote.operator / remote.action y service.name configurable. gRPC requiere un Collector; no hay exportación directa.
A continuación se muestran configuración predeterminada, carga webhook, verificación y Collector. Verifique el cuerpo HTTP original; persista y deduplique por delivery_id antes de responder 2xx. Active allow_structured_metadata en Loki 3.x, añada una fuente Loki en Grafana y consulte {service_name="remote-exchange"}. Para gRPC cambie pipeline exporters a [otlp/grpc]. Use TLS y autenticación en producción.
{"name":"collector","kind":"otel","mode":"throttled","endpoint":"https://collector.example.net:4318/v1/logs","headers":{"Authorization":"Bearer REPLACE"},"service_name":"remote-exchange","resource":{"deployment.environment.name":"production"},"batch_size":32,"interval_ms":1000,"chunk_bytes":1048576,"queue_limit":10000,"overflow_bytes":10737418240,"actions":[],"devices":[],"operators":[],"enabled":true,"ca_pem":null}
{"version":1,"events":[{"delivery_id":123,"type":"full","mode":"full","entry":{"id":42,"action":"exec","operator_name":"alice","device_id":"dev"},"data":{"kind":"binary","asset_id":"asset-uuid","path":"/data_base64","sequence":0,"offset":0,"last":true,"data_base64":"YWJj","total_size":3,"sha256":"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad","context":{"direction":"output","frame_sequence":0,"stream_offset":0}}}]}
import hashlib, hmac, time
def verify(headers, raw_body, secret):
stamp = headers['X-Remote-Timestamp']
if abs(time.time() - int(stamp)) > 300:
raise ValueError('expired webhook')
expected = 'sha256=' + hmac.new(
secret.encode(), stamp.encode() + b'.' + raw_body, hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected, headers['X-Remote-Signature']):
raise ValueError('invalid signature')
receivers:
otlp:
protocols:
http:
endpoint: 127.0.0.1:4318
processors:
batch: {}
exporters:
otlphttp/loki:
endpoint: http://loki:3100/otlp
otlp/grpc:
endpoint: telemetry.example.net:4317
headers:
Authorization: "${env:OTEL_AUTHORIZATION}"
tls:
insecure: false
service:
pipelines:
logs:
receivers: [otlp]
processors: [batch]
exporters: [otlphttp/loki]
| Campo de auditoría | Campo OTLP |
|---|---|
| service_name / resource | Resource service.name / attributes |
| entry.started_at | timeUnixNano |
| entry.id / delivery_id | remote.audit.id / remote.delivery.id |
| entry.device_id / operator_name / action | remote.device.id / remote.operator / remote.action |
| entry.tenant_id | remote.workspace.id |
| entry.started_at / ended_at / detail | remote.started_at / remote.ended_at / remote.result |
| traceId / spanId | traceId / spanId |
| data.data_base64 | body.bytesValue / remote.chunk.* |
| stored events | body.stringValue |