ADR-070: Privacidad por función (público, dependencia, privado)¶
Estado: Aceptado.
Fecha: 2026-11-07
Autores: Giampiero (mantenedor principal)
Relacionados: ADR-065 (vistas sobre _todos), ADR-067 (asistente y MCP), ADR-044 (search_path)
Contexto¶
Orfeo deja marcar un radicado como confidencial y un expediente como «solo jefe» o «dependencia», aparte de la reserva legal. OrpycaMCP solo tiene la reserva legal de la Ley 1712, en nivel_seguridad, con el gate de clearance en el WHERE.
En el Orfeo público (forks orfeo-6.2, 5.8-924, morfeo):
- El radicado guarda 0/1 en
SGD_SPUB_CODIGOy el expediente 0/1/2 enSGD_EXP_PRIVADO. - El nivel 2 del expediente no se aplica en ninguna pantalla.
- El filtro vive pantalla por pantalla y se contradice: la búsqueda compara el nombre del usuario y la lista del expediente compara el código.
- Las pantallas que cambian el nivel no verifican quién llama, y un traslado parece volver público el radicado.
Copiar la semántica sirve; copiar el mecanismo, no.
Hoy el gate de nivel_seguridad está repetido a mano en unos 176 sitios, sin predicado común, y cada consulta nueva tiene que acordarse de él.
Decisión¶
D1. Tres niveles en radicado y expediente.
| Nivel | Lo ven |
|---|---|
publico |
todos los que pasen el resto de los gates (por defecto) |
dependencia |
el autor y los miembros activos de la dependencia fijada, incluido su jefe efectivo |
privado |
solo el autor y el jefe efectivo de la dependencia fijada |
- El anexo hereda el nivel de su radicado, y el anexo de expediente (ADR-066) el de su expediente. Ninguno de los dos tiene nivel propio.
- Expediente y radicado son independientes. Un radicado que no se ve sale en el índice del expediente como esqueleto, igual que con el clearance.
- Bytes compartidos entre dueños: rige el más restrictivo, la misma regla que con
nivel_seguridad.
D2. Se suma a la reserva legal. Privacidad y nivel_seguridad se aplican juntos (AND): ninguno abre lo que el otro cierra.
D3. La dependencia se congela al marcar. Al pasar a dependencia o privado se fija privacidad_dep_codigo con la dependencia principal del autor en ese momento, y no cambia aunque el autor se traslade, ni con el flujo ni con la ubicación. Autor: registered_by del radicado y opened_by del expediente.
D4. Un solo jefe por dependencia, con encargo. El jefe se registra en la tabla dependencia_jefaturas, no como permiso:
- Por dependencia hay un titular activo y, como máximo, un encargo vigente a la vez (
desde,hasta), por ejemplo durante las vacaciones del titular. - El jefe efectivo es el encargado mientras su encargo esté vigente; si no hay encargo vigente, es el titular. Siempre hay una sola persona.
- Al vencer el encargo, el titular retoma solo, sin ningún acto adicional.
- El encargado pierde el acceso a lo privado de la dependencia en cuanto vence el encargo.
- Terminar un encargo antes de tiempo lo inactiva; nada se borra (ADR-065).
- Designan: la administración al titular, y la administración o el titular al encargado. Cada acto deja asiento en
audit_log. - Quien tiene
USUA_PERM_ADMINo es ROOT no puede ser encargado (422 encargado_es_administrador), porque el encargo le abriría lo privado que la administración no ve (D5) (decisión del dueño, 2026-11-08). La regla vive también en el motor: si un encargado pasa a ser administración,jefe_efectivoignora su encargo y la jefatura vuelve al titular. - El titular sí puede tener
USUA_PERM_ADMIN: la jefatura es un rol de la persona en su dependencia, y prohibirlo obligaría a crear cuentas duplicadas (decisión del dueño, 2026-11-08).
D5. Quién marca y quién desmarca.
- Marcan y cambian el nivel el autor y el jefe efectivo.
- El administrador (
USUA_PERM_ADMIN) no ve lo privado. Para verlo, lo desmarca apublico; ese acto deja un asiento propio (privacidad_desmarcada_por_administracion) y aparece en el historial del radicado o del expediente. - Todo cambio de nivel deja en
audit_logel valor anterior y el nuevo, de forma atómica con la escritura.
D6. El nivel no se pierde en el trámite.
- Un traslado no cambia el nivel.
- Trasladar un
privadoa alguien que no sea el autor ni el jefe efectivo responde409 privacidad_impide_traslado, y lo mismo pasa con undependenciatrasladado fuera de su dependencia. Primero hay que cambiar el nivel, con su asiento. - Así nadie tiene en su bandeja algo que no puede ver.
D7. El filtro va en el motor.
- Las vistas
radicados,expedientes,anexosyexpediente_anexos(ADR-065) agregan la condición de privacidad sobre la identidad de la petición,current_setting('orpycamcp.user_id', true). - La puerta de conexión de cada servicio (
get_db_connoget_tenant_conn) la fija confijar_identidada partir delX-User-Idfirmado por el gateway (auth_users.id). Va en la sesión y no conSET LOCAL, porque las puertas entregan la conexión sin una transacción de petición. Se quita en elfinallyde la puerta (SQL_CERRAR_SESION) y el pool haceRESET ALLal devolverla. - Si falta la identidad, la vista solo muestra
publico(fail-closed). Las rutas públicas (OAI, CMIS, ventanilla, verificación) no la fijan. - El predicado es una función por tenant,
privacidad_visible, definida en auth-service (migración 024) porque depende deurd_usuaroldep. EsSTABLE, sinSET search_path, para que el planificador la expanda. - Las vistas son
security_barrier(document 066, archive 104). - Se usan vistas y no RLS porque el rol de la pila de desarrollo es superusuario y RLS lo saltaría: las pruebas pasarían en vacío.
- Procesos de sistema. Los que deben ver todo (reconciliación del índice, vencimientos, barridos, consumidores de eventos, ingesta del RAG, renovación LTA, internos sin persona) declaran
orpycamcp.sistemaen su conexión confijar_sistema, y esa conexión queda sin identidad de persona. El proceso tiene que estar en la lista cerrada deorpycamcp_common/procesos_sistema.py, y un guardián deshared/testsfalla si se anuncia uno fuera de ella o si se nombra una tabla_todosfuera de los lectores declarados. - Se prefirió el modo sistema a leer
_todosporque esos repositorios son los mismos que usan las personas (unas 300 sentencias) y porque_todospierde el filtro de inactivación de ADR-065. - Los flujos HTTP que una persona dispara y que deben ver todo el expediente (foliado e índice, transferencia, disposición, exportación) primero comprueban que la persona ve el objeto y solo después entran en modo sistema.
D8. Lo privado y lo de dependencia no salen del servidor.
- El asistente y el MCP reciben solo el esqueleto: número, tipo, fecha y nivel, sin asunto, remitente, contenido ni anexos. Es el mismo recorte que ya se aplica a lo reservado.
- La ingesta del RAG guarda el nivel y la recuperación lo cruza con el radicado vivo.
- OAI, CMIS y la verificación pública exigen
publicoen el ancla_PUBLIC.
D9. Una dependencia sin jefe no opera (decisión del dueño, 2026-11-07).
- Una dependencia se crea con su titular. Si tiene una sola persona, esa persona es la titular.
- Una dependencia es operativa solo si tiene un titular activo: usuario activo y con membresía activa en ella. El estado se deriva y no se guarda, así que no hace falta reconciliarlo. La salida del titular ya deja su asiento: cuando lo relevan sin reemplazo, cuando se da de baja (ADR-068) o cuando pierde la membresía.
- Mientras no es operativa, la dependencia aparece inactiva y no recibe trabajo nuevo:
- los traslados, asignaciones y radicaciones hacia ella responden
409 dependencia_sin_jefe; - tampoco se puede marcar algo
dependenciaoprivadoen ella. - Lo que ya estaba en la dependencia se conserva, y vuelve a moverse cuando la administración designa un titular.
- El encargo cubre una ausencia temporal del titular (vacaciones), no su salida: si el titular se va, la dependencia deja de operar aunque haya un encargo vigente.
- La administración ve la lista de dependencias sin jefe.
- Las dependencias que ya existen sin titular quedan inactivas hasta que la administración lo designe por la API o por la pantalla. No hay backfill.
D10. La privacidad sigue a quien tiene el documento (decisión del dueño, 2026-11-08; enmienda D1, D3 y D6).
- En un radicado, la privacidad está anclada a una persona: el poseedor actual, que es quien tiene el paso activo del flujo. Si el radicado no está en ningún flujo, el ancla es el autor.
privado: lo ven esa persona y el jefe efectivo de la dependencia del ancla.dependencia: lo ven los miembros de esa dependencia.- El autor deja de verlo cuando ya no lo tiene, salvo que sea el jefe.
- Trasladar, asignar, reasignar o devolver cambia el ancla: el nuevo destinatario y su dependencia. Si el paso va a una dependencia sin persona nombrada, el ancla es su jefe efectivo.
- El traslado no depende del nivel, porque es un asunto de procedimiento: se retira
409 privacidad_impide_traslado. - Cada cambio de ancla deja asiento en la misma transacción del traslado.
- En un expediente no hay poseedor: el ancla sigue siendo quien lo abrió.
- Reasignar es un acto normal, sea privado o no, y la marca se conserva. No hay un proceso de sistema para repartir los privados de quien sale: la cascada de la administración no toca lo que no ve (solo informa cuántos omitió), y esos los reasigna quien los tiene (decisión del dueño, 2026-11-08).
D11. Al cerrar el expediente se retira el filtro de privacidad (decisión del dueño, 2026-11-08).
- El cierre deja en
publicoel expediente y los radicados que contiene. Cada cambio deja su asiento (privacidad_retirada_al_cerrar) en la transacción del cierre. - Lo cerrado ya no se puede volver a marcar.
- Con esto, la transferencia, la valoración, el FUID y la eliminación trabajan siempre sobre lo público. La privacidad solo protege mientras el asunto está en trámite.
- La reserva legal (
nivel_seguridad) no cambia con el cierre. - La retención legal sobre algo aún en trámite y privado es un acto de custodia sin contenido, pendiente del concepto del asesor.
D12. Para cerrar el expediente, sus radicados deben estar archivados y conformes a la TRD (decisión del dueño, 2026-11-08, como en los últimos ajustes de Orfeo Veeduría).
- Un expediente puede recibir radicados de otras dependencias. Para cerrarlo, todos sus radicados deben:
- estar archivados, es decir, haber terminado su trámite por la vía de
finalizardel flujo, que deja el radicado enclosed, o estar anulados; - cumplir la TRD del expediente: su tipo documental pertenece a la serie o subserie del expediente.
- Si no se cumple, el cierre responde
409 radicados_sin_archivaro409 radicados_fuera_de_trd, con el número de radicados pendientes y sin su contenido. document-service repite la condición de archivado al retirar la privacidad, para que ninguna otra llamada interna la salte. - Un radicado de un expediente cerrado, transferido o inactivado no se retipifica (
409 radicado_en_expediente_cerrado): queda con el tipo con que se juzgó el cierre. La regla se consulta en vivo, así que dejaría de aplicar si el expediente se reabriera (hoy no hay reapertura) (decisión del dueño, 2026-11-08). - Archivar lo hace quien tiene el radicado: es él quien lo entrega. Desde ese momento el radicado ya no le pertenece a una persona sino al archivo, que es un estado. Por eso el cierre (D11) no le quita la privacidad a nadie que no la haya entregado antes.
- Esto resuelve el hallazgo de auditoría en el que un tercero cerraba un expediente para volver públicos radicados ajenos que seguían en trámite.
Consecuencias¶
- Las columnas nuevas nacen en
publicopor defecto. No hay backfill (regla del proyecto). - Toca la conexión de 9 servicios y unos 27 procesos de sistema que hay que declarar uno a uno. La auditoría de seguridad no es opcional (fuga entre personas del mismo tenant).
- El gate de
nivel_seguridadno se mueve al motor en este ADR. Queda abierta la posibilidad de hacerlo con el mismo mecanismo. - Las llamadas internas entre servicios deben propagar la identidad; sin ella ven solo lo público.
- Los informados de Orfeo no existen en OrpycaMCP y no amplían la vista.
- Los conteos agregados de los reportes (AGN y control del consecutivo) se calculan en modo sistema, como proceso declarado (
document.reportes): sin contenido, cuentan todo el fondo. Con un rango muy corto se podría inferir que existe un privado; se acepta, porque la estadística y el control del consecutivo deben ser completos (Acuerdo AGN 001/2024).