@ingeniomaps/cauce 0.78.0 → 0.80.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -22,3 +22,24 @@ node tools/ops.js integration promote . jira KEY-123
22
22
  El motor compara base reconciliada, remoto actual y curación local. Usa `reset` para adoptar el remoto,
23
23
  `reconcile` para conservar la edición local sobre la nueva base y `rebase` para reparar hashes mecánicos.
24
24
  Ninguno de esos comandos escribe en el proveedor.
25
+
26
+ ## Un proveedor propio
27
+
28
+ Cauce trae Jira. Para conectar otra herramienta, el adaptador se escribe acá, en la instancia, sin tocar
29
+ Cauce:
30
+
31
+ 1. Crear `integrations/<nombre>/` con su `config.json` y el adaptador, por ejemplo `adapter.js`.
32
+ 2. Registrarlo en `config.json` con la ruta, relativa a `integrations/<nombre>/`:
33
+ `"adapter": "./adapter.js"`. Un nombre sin `./` —`"jira"`— es un adaptador de Cauce.
34
+ 3. `node tools/ops.js integration enable . <nombre>` lo conecta y `integration check .` lo valida.
35
+
36
+ El adaptador exporta `contract: 1` y tres funciones:
37
+
38
+ - `validateConfig(config, errors)` valida sin conectarse, y empuja a `errors` lo que impide correr.
39
+ - `fetchItems(config, options)` hace la lectura paginada completa del proveedor.
40
+ - `normalizeFixture(payload, config)` usa el mismo normalizador sin red, para pruebas e importaciones.
41
+
42
+ `check` rechaza un adaptador con otra versión o sin alguna de las funciones, y una ruta que salga de
43
+ `integrations/<nombre>/`. Puede ser CommonJS o ESM. El motor lo ejecuta con los mismos permisos que el
44
+ CLI: la contención es de ruta, no de capacidad. Staging, revisión, promoción y validación son los mismos
45
+ que para Jira; el adaptador sólo traduce la API del proveedor.
@@ -18,4 +18,56 @@ Recomendados, sin molde: escribilos con la forma que le sirva a este proyecto.
18
18
  La lista no es cerrada. Todo hecho de negocio o producto que cambie lentamente vive acá —una guía de
19
19
  marca, un manual de operación, un pipeline de contenido— aunque no tenga una línea propia arriba.
20
20
 
21
+ Opcional, con forma fija: `secrets.json`, el contrato de secretos que la empresa comparte entre sus
22
+ repositorios. Ver «Secretos compartidos» abajo.
23
+
21
24
  Principio: cada hecho tiene un dueño. Enlaza en vez de copiar información que ya vive en otro lugar.
25
+
26
+ ## Secretos compartidos
27
+
28
+ Cuando varios servicios usan el mismo gestor de secretos —Infisical, Vault, Doppler—, terminan con los
29
+ mismos scripts y workflows copiados en cada repositorio, y la copia que se quedó atrás no avisa.
30
+ `secrets.json` declara ese contrato una vez y `node tools/ops.js secrets check .` lo compara contra cada
31
+ servicio **sin conectarse a nada**. Cauce no conoce ningún gestor: lo que habla con él es de la empresa.
32
+
33
+ ```json
34
+ {
35
+ "schemaVersion": 1,
36
+ "accounts": { "principal": { "url": "https://app.infisical.com" } },
37
+ "projects": { "tienda": { "account": "principal" } },
38
+ "identities": {
39
+ "local-dev": { "account": "principal", "source": "file", "file": "~/.config/acme/local-dev.env" },
40
+ "ci": { "account": "principal", "source": "ci-secret" }
41
+ },
42
+ "shared": { "check-schema": "organization/secrets/check-schema.py" },
43
+ "services": {
44
+ "api": { "root": "api", "project": "tienda", "identity": "local-dev",
45
+ "files": { "scripts/check-schema.py": "check-schema" } }
46
+ }
47
+ }
48
+ ```
49
+
50
+ - **Nunca guarda un valor.** Una clave con forma de secreto —`token`, `password`, `secret`— es un error.
51
+ Las entradas pueden llevar otros campos que lean los scripts de la empresa (un id de proyecto, sus
52
+ ambientes); el chequeo no los mira.
53
+ - **Una identidad por nivel de acceso, no por repositorio.** Compartir credenciales es apuntar al mismo
54
+ alias, y rotar es cambiar un archivo. `source: file` es un archivo **fuera de todo repositorio**, que
55
+ carga una persona; `source: ci-secret` vive en el CI y no lleva ruta.
56
+ - **`shared` guarda la copia canónica** de cada archivo compartido, dentro de esta instancia, y
57
+ `services.<nombre>.files` dice dónde va en el servicio. Cada `root` es una raíz de `ops.config.json`.
58
+ - **Varios proyectos, una instancia.** El chequeo compara los servicios de esta instancia; para que el
59
+ esqueleto de un proyecto y los servicios de otro se midan contra lo mismo, los dos van como raíces acá.
60
+
61
+ `secrets check` falla si una copia no coincide con la canónica —y dice el `cp` que la pone al día—, si
62
+ falta, si una referencia no cierra o si una credencial está dentro de un repositorio. Una identidad que
63
+ no está en esta máquina es una advertencia: en el CI no tiene por qué estar.
64
+
65
+ El recorrido:
66
+
67
+ - **Adoptar un servicio**: declararlo, copiar los archivos de `shared` y correr `secrets check`. Cargar
68
+ la identidad en su archivo y los secretos del CI lo hace una persona: el guard de secretos frena que
69
+ un agente escriba un `.env.*`, y es lo correcto.
70
+ - **Mantener**: se cambia la copia canónica, `secrets check` lista los servicios que quedaron atrás y
71
+ cada uno se actualiza con un commit en su repositorio.
72
+ - **Rotar**: se reemplaza el archivo de la identidad; los servicios que la usan no cambian.
73
+ - **Dar de baja**: se saca el servicio de `services` y se borran sus copias en su repositorio.
@@ -47,3 +47,4 @@ archivo lo mantiene Cauce, así que una fila agregada acá se perdería en el pr
47
47
  - [OPS-004](system/OPS-004-promocion-humana-y-evidencia-verificable.md): promoción controlada y verificable.
48
48
  - [OPS-005](system/OPS-005-catalogo-en-el-paquete.md): el catálogo viaja dentro del paquete.
49
49
  - [OPS-006](system/OPS-006-ceremonia-por-superficie.md): la ceremonia escala con la superficie del cambio.
50
+ - [OPS-007](system/OPS-007-contrato-de-secretos-compartido.md): un contrato de secretos compartido, sin gestor.
@@ -0,0 +1,62 @@
1
+ # OPS-007 — Un contrato de secretos compartido, sin conocer ningún gestor
2
+
3
+ **Estado:** Aceptado
4
+ **Fecha:** 2026-09-10
5
+
6
+ > Decide qué parte del manejo de secretos es de la base; el gestor, sus scripts y su CI siguen siendo de
7
+ > la empresa.
8
+
9
+ ## Contexto
10
+
11
+ Una empresa que adopta un gestor de secretos termina con el mismo modelo copiado en cada repositorio:
12
+ la identidad con que se lee el gestor, un script que compara el gestor contra el contrato de variables y
13
+ los workflows que avisan en el PR. Nada dice qué servicio usa qué cuenta ni qué identidad, y nada detecta
14
+ que una copia se quedó atrás. En una instancia real se contaron dieciséis copias de un mismo script en
15
+ tres variantes, y el arreglo que tenía una sola no había llegado ni al esqueleto del que salen los
16
+ servicios nuevos.
17
+
18
+ `integrations/` no sirve para esto: su ciclo es de contenido de trabajo que baja a planning (OPS-003), y
19
+ su README prohíbe guardar secretos ahí.
20
+
21
+ ## Decisión
22
+
23
+ **La base declara y compara; no se conecta, no genera y no conoce ningún gestor.**
24
+
25
+ - La declaración vive en `organization/secrets.json`, que es del proyecto: cuentas, proyectos,
26
+ identidades, servicios y los archivos que comparten. Nunca un valor: una clave con forma de secreto es
27
+ un error.
28
+ - Una identidad es un nivel de acceso, no un repositorio. `source: file` es un archivo **fuera de todo
29
+ repositorio**, que carga una persona; `source: ci-secret` vive en el CI.
30
+ - La copia canónica de cada archivo compartido vive en la instancia. `ops secrets check` compara cada
31
+ copia de cada servicio contra ella por hash, sin red, y dice el `cp` que la pone al día. Cauce no
32
+ escribe en los repositorios de producto.
33
+ - El chequeo compara los servicios de una instancia. Una empresa con varios proyectos los declara como
34
+ raíces de la misma instancia, que es lo que hace que un esqueleto y sus derivados se midan contra lo
35
+ mismo.
36
+
37
+ ## Alternativas consideradas
38
+
39
+ - **Un adaptador de la empresa que genere los archivos**: flexible, pero obliga al motor a cargar código
40
+ de la instancia y a versionar una interfaz, para resolver lo mismo que una copia comparada por hash.
41
+ - **El adaptador como paquete versionado que cada instancia instala**: mantiene una instancia por
42
+ proyecto a cambio de publicar y versionar un paquete más.
43
+ - **Un workflow reutilizable compartido**: una sola copia, pero supone GitHub y una organización, y deja
44
+ a la base atada a un proveedor de CI.
45
+ - **Hacerlo un proveedor de `integrations/`**: su staging, reconciliación y promoción no se aplican a
46
+ secretos.
47
+
48
+ ## Consecuencias
49
+
50
+ **Ganamos:** la copia que se quedó atrás se ve, con su arreglo al lado; rotar una identidad compartida
51
+ es cambiar un archivo; y la base sigue sin saber de ningún gestor.
52
+
53
+ **Costos que aceptamos:** un arreglo sigue siendo un commit por repositorio —el chequeo los lista, no los
54
+ aplica—. Juntar varios proyectos en una instancia comparte también su `planning/`. Y una credencial
55
+ dentro de un repositorio es un error aunque esté ignorada por git: es la copia por repositorio que esta
56
+ decisión viene a sacar.
57
+
58
+ ## Estado de implementación
59
+
60
+ Implementado en 0.80.0: `organization/secrets.json`, `ops secrets check` y el recorrido documentado en
61
+ `organization/README.md`. Cauce no trae adaptadores ni un workflow de ejemplo que corra el chequeo en CI;
62
+ correrlo ahí es un paso de la empresa.