@kwirthmagnify/kwirth-docs-pinocchio 0.2.31

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.
package/_sidebar.md ADDED
@@ -0,0 +1,21 @@
1
+ - [Pinocchio](index.md)
2
+
3
+ - **Conceptos**
4
+ - [Introducción y modelo mental](user/01-introduction.md)
5
+ - [Cómo funciona](user/00-how-it-works.md)
6
+ - [Triggers, versiones y prompts](user/03-concepts.md)
7
+
8
+ - **Guía de usuario**
9
+ - [Recorrido por la UI](user/02-ui-tour.md)
10
+ - [Configurar triggers](user/04-triggers.md)
11
+ - [Leer los findings](user/05-findings.md)
12
+ - [El Playground](user/06-playground.md)
13
+ - [Import / Export de triggers](user/07-import-export.md)
14
+
15
+ - **Guía de administrador**
16
+ - [Instalación](admin/01-setup.md)
17
+ - [Providers y modelos de IA](admin/02-ai-config.md)
18
+ - [Tools y pasos del agente](admin/03-tools.md)
19
+ - [Plantillas de prompt](admin/04-prompts.md)
20
+ - [Permisos y acceso](admin/05-rbac.md)
21
+ - [Límites conocidos](admin/06-limits.md)
@@ -0,0 +1,98 @@
1
+ # Instalación
2
+
3
+ Pinocchio es un plugin de tipo **canal**. Se instala como cualquier otro plugin de kwirth, pero tiene una
4
+ dependencia dura y una condición previa que conviene resolver antes de que los usuarios abran el canal.
5
+
6
+ ## Requisitos
7
+
8
+ | Requisito | Detalle |
9
+ |-----------|---------|
10
+ | Provider `business` | **Obligatorio.** El manifiesto declara `requiresExtension: ["provider:business:0.1.6"]`. kwirth no deja instalar el plugin sin él. |
11
+ | Providers `events` y `metrics` | Los aporta el core. No hay nada que instalar. |
12
+ | Almacenamiento del canal | El canal declara `storage: true`: kwirth le da un almacén donde guarda la clave `pinocchio-config`. |
13
+ | Un provider de IA y un LLM | **No es un requisito de instalación, pero sí de uso.** Sin ellos el canal arranca y no sirve para nada. Ver [Providers y modelos de IA](02-ai-config.md). |
14
+ | Tipo de clúster | El canal declara `sources: [KUBERNETES]`. No funciona sobre orígenes que no sean Kubernetes. |
15
+
16
+ **Reinicio:** el manifiesto declara `requiresRestart: false`. Instalarlo o actualizarlo no obliga a reiniciar
17
+ el backend de kwirth.
18
+
19
+ ## Instalar en producción
20
+
21
+ Desde la UI de administración de kwirth, en el gestor de plugins: búscalo en el marketplace, o instálalo
22
+ desde la URL del tarball publicado:
23
+
24
+ ```
25
+ https://registry.npmjs.org/@kwirthmagnify/kwirth-plugin-pinocchio/-/kwirth-plugin-pinocchio-<version>.tgz
26
+ ```
27
+
28
+ Instala **antes** el provider `business`; si no, la instalación del plugin se rechaza por dependencia no
29
+ satisfecha.
30
+
31
+ ## Instalar en desarrollo
32
+
33
+ En un entorno de desarrollo, el backend carga los plugins desde disco leyendo `back/kwirth-dev.json`:
34
+
35
+ ```json
36
+ {
37
+ "plugins": {
38
+ "pinocchio": "../plugins/pinocchio/dist"
39
+ },
40
+ "providers": {
41
+ "business": "../providers/business/dist"
42
+ },
43
+ "docs": {
44
+ "pinocchio": "../plugins/pinocchio/docs/pinocchio.tgz"
45
+ }
46
+ }
47
+ ```
48
+
49
+ La entrada de `docs` es la que hace que **esta guía** quede accesible desde la propia UI de kwirth.
50
+
51
+ ## Construir desde fuente
52
+
53
+ ```bash
54
+ cd plugins/pinocchio
55
+ npm install
56
+ npm run build # front.js + back.js + docs/pinocchio.tgz + dist/package.json
57
+ npm run watch # reconstruye todo en cada cambio, la guía incluida
58
+ npm run docs # sólo reempaqueta la guía
59
+ ```
60
+
61
+ `build` y `watch` cubren **las tres piezas**: el bundle de front, el de back y el tarball de la guía. No hace
62
+ falta acordarse de empaquetar la documentación por separado.
63
+
64
+ Con `watch` en marcha:
65
+
66
+ - un cambio en `src/front/` lo recoge el navegador solo (el front sondea `front.js` cada 2 s);
67
+ - un cambio en `src/back/` **requiere reiniciar el core**, que cachea el módulo de back;
68
+ - un cambio en `docs/guide/` reconstruye el tarball al vuelo, pero el core lo instala al arrancar, así que se
69
+ ve tras reiniciarlo.
70
+
71
+ ## Qué hace el canal al arrancar
72
+
73
+ Cada vez que un usuario abre el canal, la instancia del backend:
74
+
75
+ 1. Se suscribe a los providers `metrics`, `business` (espacios `customers.status`, `branches.status`,
76
+ `launch.immediate`) y `events` (los 11 kinds que vigila).
77
+ 2. Lee los **providers de IA** del almacén común (`kwirth-ai-providers`) y construye los modelos.
78
+ 3. Lee su propia configuración (`pinocchio-config`) del almacén del canal.
79
+ 4. Lee los **LLMs** del almacén común (`kwirth-ai-llms`); si hay alguno, sustituye a los que tuviera guardados.
80
+
81
+ > ⚠️ **Los puntos 2 y 4 sólo ocurren al arrancar el canal.** Si un administrador crea un provider desde los
82
+ > menús *AI Providers* del core con el canal ya abierto, ese canal no lo verá hasta que se cierre y se vuelva
83
+ > a abrir. Ver [Límites conocidos](06-limits.md).
84
+
85
+ ## Comportamiento del canal
86
+
87
+ Lo que el canal declara al core, y lo que significa para el usuario:
88
+
89
+ | Propiedad | Valor | Efecto |
90
+ |-----------|-------|--------|
91
+ | `routable` | `false` | No se puede dirigir a un recurso concreto: siempre es de clúster. |
92
+ | `pauseable` | `true` | Se puede pausar y continuar. |
93
+ | `modifiable` | `false` | No se puede reconfigurar la instancia en caliente. |
94
+ | `reconnectable` | `true` | Sobrevive a una reconexión del websocket. |
95
+ | `resourced` | `false` | No selecciona namespaces ni pods al arrancar. |
96
+ | `cluster` | `true` | Es un canal de ámbito de clúster. |
97
+
98
+ No tiene diálogo de *Setup*: no hay nada que elegir antes de arrancarlo.
@@ -0,0 +1,125 @@
1
+ # Providers y modelos de IA
2
+
3
+ Esta es la configuración que hay que resolver **antes** de que Pinocchio sirva para algo. Y lo primero que
4
+ hay que entender es que **no es configuración de Pinocchio**.
5
+
6
+ ## La configuración de IA es de kwirth, no del plugin
7
+
8
+ Pinocchio no guarda sus propios providers ni sus propios modelos. Los lee del **almacén común de kwirth**,
9
+ compartido con todos los demás plugins que usan IA:
10
+
11
+ | Qué | Clave | Dónde |
12
+ |-----|-------|-------|
13
+ | Providers (con las API keys) | `kwirth-ai-providers` | **Secret** de Kubernetes |
14
+ | LLMs | `kwirth-ai-llms` | **ConfigMap** de Kubernetes |
15
+
16
+ Las consecuencias prácticas son tres:
17
+
18
+ 1. Si ya configuraste un provider para otro plugin, **Pinocchio lo ve**. No hay que repetir nada.
19
+ 2. Si lo configuras desde Pinocchio, **lo verán los demás**. Los diálogos del grupo *AI* del menú Config
20
+ (*AI providers* / *AI models*) son literalmente los mismos componentes comunes
21
+ (`AiConfigProvider` / `AiConfigLlm`) que usa el core en sus menús *AI Providers* / *AI Models*.
22
+ 3. Las claves viven en un **Secret**, no en un ConfigMap. Quien tenga permiso de lectura sobre los Secrets del
23
+ namespace de kwirth puede leerlas.
24
+
25
+ ## Provider
26
+
27
+ Un *provider* es una cuenta contra un proveedor de IA: el adaptador y la clave.
28
+
29
+ | Campo | Qué es |
30
+ |-------|--------|
31
+ | `Name` | Identificador libre de **esta** instancia de provider (`openai-prod`, `huawei-maas`). Es lo que referencian los LLMs. |
32
+ | `Type` | El adaptador de SDK. Lista cerrada: `google`, `openai`, `openrouter`, `mistral`, `groq`, `deepseek`, `anthropic`, `openai-compat`. |
33
+ | `API Key / Token` | La credencial. Campo de contraseña con botón de ojo para revelarla. |
34
+ | `Base URL` | **Sólo para `openai-compat`.** La URL base de la API compatible con OpenAI (Huawei MaaS, vLLM, LM Studio…). Debe apuntar a la raíz; el adaptador añade `/v1` si falta. |
35
+
36
+ ![El diálogo de providers, con el contador de modelos cargados por provider](../images/admin-ai-provider.png)
37
+
38
+ Al elegir un `Type`, si el nombre está vacío se rellena solo con el tipo. **Déjalo así salvo que tengas una
39
+ razón** — ver la advertencia del final de la página.
40
+
41
+ **Load models** consulta al proveedor el catálogo de modelos disponibles y lo cachea en el provider. La
42
+ llamada la hace el **core** de kwirth (`POST /core/aiconfig/loadmodels`), no el plugin, porque es el core
43
+ quien tiene los adaptadores de cada SDK. Si el botón no aparece o falla, revisa que la clave sea válida.
44
+
45
+ Sin modelos cargados, el provider aparece **deshabilitado** en el desplegable del diálogo de LLM.
46
+
47
+ > ⚠️ El botón **Export** de este diálogo descarga `kwirth-providers.json` **con las API keys en claro**.
48
+ > Trátalo como un secreto: no lo subas a un repositorio ni lo mandes por correo.
49
+
50
+ ## LLM
51
+
52
+ Un *LLM* es un modelo concreto de un provider concreto, con sus parámetros. Es lo que referencian los
53
+ triggers, por su `LLM ID`.
54
+
55
+ | Campo | Qué es |
56
+ |-------|--------|
57
+ | `LLM ID` | El identificador que usarán los triggers. **Elígelo con cuidado**: es lo que viaja en un export de triggers. |
58
+ | `Provider` | Uno de los providers configurados. Sólo se pueden elegir los que tienen modelos cargados. |
59
+ | `Model` | Desplegable con los modelos cargados del provider; si el provider no tiene catálogo, se convierte en una caja de texto libre. |
60
+ | `Model temperature` | Temperatura. ⚠️ Pinocchio la **recorta al rango 0–1** antes de llamar al modelo. |
61
+ | `Input / Output cost / M tokens` | Coste informativo por millón de tokens. No lo usa el motor: es para que puedas calcular la factura. |
62
+ | `Use provider API Key` | Si está marcado, usa la clave del provider. Si no, puedes darle una clave específica a este LLM. |
63
+
64
+ ![El diálogo de LLMs; cada entrada lista su id y el provider del que cuelga](../images/admin-ai-llm.png)
65
+
66
+ Para análisis de seguridad, **temperatura baja** (0 – 0.2). Lo que quieres es un auditor consistente, no uno
67
+ creativo.
68
+
69
+ ## Salida estructurada: no todos los modelos valen
70
+
71
+ Los triggers de tipo `artifact` exigen al modelo una respuesta que cumpla un esquema JSON estricto. Un modelo
72
+ pequeño o antiguo que no soporte *structured output* fallará sistemáticamente, y lo verás como un análisis con
73
+ dos findings `critical` y el error del proveedor.
74
+
75
+ Pinocchio añade opciones específicas por proveedor para que esto funcione:
76
+
77
+ | Proveedor | Opciones que envía |
78
+ |-----------|--------------------|
79
+ | `google` | `{ google: { structuredOutputs: true } }` |
80
+ | `groq` | `{ groq: { structuredOutputs: true } }` |
81
+ | `mistral` | `{ mistral: { strictJsonSchema: true, structuredOutputs: true } }` |
82
+ | cualquier otro | `{ openai: {} }` |
83
+
84
+ > ⚠️ **Gotcha importante.** Esa tabla se elige por el **`Name`** del provider, no por su `Type`. El modelo
85
+ > sí se construye correctamente por el tipo, pero las opciones de salida estructurada no.
86
+ >
87
+ > Es decir: un provider de tipo `google` llamado `google` funciona; el **mismo** provider llamado
88
+ > `gemini-prod` construye el modelo bien pero **pierde** el `structuredOutputs: true`, y los triggers
89
+ > `artifact` pueden empezar a fallar sin motivo aparente.
90
+ >
91
+ > **Recomendación: deja el `Name` del provider igual que su `Type`** mientras no tengas que crear dos cuentas
92
+ > del mismo proveedor. Si necesitas varias, usa una con el nombre canónico para Pinocchio. Está recogido en
93
+ > [Límites conocidos](06-limits.md).
94
+
95
+ ## El orden importa
96
+
97
+ El menú Config va habilitando entradas conforme cumples los requisitos:
98
+
99
+ ```
100
+ AI ▸ AI providers (siempre)
101
+ |
102
+ v con >= 1 provider
103
+ AI ▸ AI models
104
+ |
105
+ v con >= 1 LLM
106
+ Trigger
107
+ ```
108
+
109
+ Si un usuario te dice que "Trigger está en gris", no es un fallo del plugin: falta el LLM.
110
+
111
+ ## Cuándo lee el canal esta configuración
112
+
113
+ **Sólo al arrancar la instancia del canal.** Si creas un provider o un LLM desde los menús *AI Providers* /
114
+ *AI Models* del core mientras un usuario tiene el canal abierto, ese usuario no lo verá hasta cerrar y
115
+ reabrir el canal.
116
+
117
+ En cambio, si lo creas **desde el propio menú Config de Pinocchio**, el canal sí se entera al momento: el
118
+ plugin manda un `PROVIDERSSET`/`CONFIGSET` que reescribe el almacén y recarga los modelos.
119
+
120
+ ## Multi-clúster
121
+
122
+ La configuración de IA **no viaja entre clústeres**. Los menús del core escriben en el backend **local**,
123
+ pero un canal abierto contra otro kwirth lee el almacén de **ese** clúster. Si operas una federación, tienes
124
+ que configurar providers y LLMs en cada clúster, y con los **mismos ids de LLM** si quieres poder mover
125
+ triggers entre ellos. Ver [Límites conocidos](06-limits.md).
@@ -0,0 +1,131 @@
1
+ # Tools y pasos del agente
2
+
3
+ Las **tools** son lo que separa a Pinocchio de un chat al que le pegas un YAML. Son funciones reales,
4
+ ejecutadas dentro del clúster, que el modelo puede invocar por su cuenta mientras razona. Esta página
5
+ explica el catálogo, cómo se controla el gasto y —lo más importante— **por qué `Auto` es peligroso**.
6
+
7
+ ## Cómo funciona la ejecución con tools
8
+
9
+ Cuando un trigger se dispara, el backend construye una llamada al modelo con:
10
+
11
+ - el `system` y el `prompt`,
12
+ - el subconjunto de tools que la versión declara,
13
+ - un tope de pasos: `stopWhen(stepCountIs(steps))`.
14
+
15
+ El modelo decide si necesita datos. Si los necesita, llama a una tool; el backend la ejecuta, le devuelve el
16
+ resultado, y eso cuenta como **un paso**. El ciclo se repite hasta que el modelo responde o se agotan los
17
+ pasos. Cada paso es una llamada de pago, con todo el contexto acumulado en la entrada.
18
+
19
+ > Si `steps` es 0 o está vacío, el backend **no usa 0**: usa **15**. Es el error de configuración más caro
20
+ > que se puede cometer en este plugin.
21
+
22
+ Todas las llamadas y respuestas de tools se registran como trazas en el log del backend
23
+ (`[pinocchio] tool <nombre> ...`), así que se puede auditar qué consultó el modelo.
24
+
25
+ ## El catálogo
26
+
27
+ El catálogo lo aporta `kwirth-common-ai` y es el mismo para todos los plugins de IA de kwirth. El canal se lo
28
+ envía al front al arrancar, así que el selector siempre muestra lo que el backend soporta de verdad.
29
+
30
+ ![El selector de tools, con la descripción de cada una](../images/tool-selector.png)
31
+
32
+ ### Lectura — inventario y configuración
33
+
34
+ | Tool | Para qué |
35
+ |------|----------|
36
+ | `list_namespaces` | Namespaces con estado y etiquetas. |
37
+ | `get_cluster_data` | Nombre, sabor (AKS/EKS/GKE/k3s/k3d), vCPUs, memoria, nodos. |
38
+ | `get_node_data` | Configuración de los nodos (nombre, IP). |
39
+ | `get_workload_data` | Todos los workloads del clúster; filtrable por namespace. |
40
+ | `get_space_data` | Todo lo que hay en un namespace. |
41
+ | `list_services` / `get_service_yaml` | Services del clúster / manifiesto completo de uno. |
42
+ | `list_ingresses` / `get_ingress_yaml` | Ingresses / manifiesto completo de uno. |
43
+ | `get_pod_yaml` / `get_deployment_yaml` | Manifiesto completo de un Pod o Deployment. |
44
+
45
+ ### Lectura — diagnóstico
46
+
47
+ | Tool | Para qué |
48
+ |------|----------|
49
+ | `describe_pod` | Equivalente a `kubectl describe pod`: motivo de espera/terminación, exitCode (OOMKilled, CrashLoop…), reinicios, probes. |
50
+ | `get_pod_logs` | Logs del contenedor. Con `previous:true`, los de la instancia que petó — la causa raíz de un CrashLoopBackOff. |
51
+ | `get_cluster_events` / `get_object_events` | Eventos de Kubernetes del clúster o de un objeto concreto. |
52
+ | `get_rollout_history` | Revisiones de un Deployment: qué imagen y qué env tenía cada una. Para ver **qué cambió** justo antes de romperse. |
53
+ | `get_configmap` / `get_secret` | Datos de un ConfigMap / claves de un Secret (**valores redactados**). Un cambio de valor no genera revisión de rollout: por eso hay que mirarlos aparte. |
54
+ | `get_workload_config_refs` | Qué ConfigMaps y Secrets consume un Deployment, con su fecha de última modificación. |
55
+ | `get_source_file` | Trae un fichero de un repo Git (GitHub/GitLab) en una ref, para seguir un stack trace hasta el fichero:línea culpable. |
56
+ | `get_certificate_info` | Detalles del certificado TLS de un host: emisor, validez, SANs, huella. |
57
+
58
+ ### Lectura — uso de recursos
59
+
60
+ `get_cluster_usage`, `get_node_usage`, `get_deployment_usage` y sus versiones históricas
61
+ `get_prev_cluster_usage`, `get_prev_node_usage`, `get_prev_deployment_usage`, `get_prev_space_data`.
62
+
63
+ Estas tools se alimentan del **buffer de métricas** del canal: las últimas 100 lecturas del provider
64
+ `metrics`. Si acabas de abrir el canal, el histórico estará casi vacío.
65
+
66
+ ### Escritura — ⚠️ modifican el clúster
67
+
68
+ | Tool | Qué hace |
69
+ |------|----------|
70
+ | `add_replica` / `remove_replica` | Escala un Deployment arriba o abajo (mínimo 1 réplica). |
71
+ | `restart_deployment` | Rollout-restart de un Deployment. |
72
+ | `delete_pod` | Borra un pod; su controlador lo recrea. |
73
+ | `add_node` / `remove_node` | Añade o quita un nodo del clúster (en k3d, vía `k3d node create/delete`). |
74
+ | `start_node` / `stop_node` | Arranca o para un nodo. |
75
+
76
+ ### De prueba
77
+
78
+ `times_two` y `father_of` son tools de juguete para verificar que la cadena de tool-calling funciona. No las
79
+ pongas en un trigger real.
80
+
81
+ ## El interruptor `Auto`: léelo antes de usarlo
82
+
83
+ El selector de tools tiene un interruptor **Auto**. Lo que hace **depende de dónde estés**, y la diferencia
84
+ es importante:
85
+
86
+ **En un trigger real** — `Auto` entrega al modelo el **catálogo completo, sin filtrar**. Sin selección previa
87
+ y, sobre todo, **sin quitar las tools de escritura**.
88
+
89
+ > ⚠️ **Un trigger con `Auto` activado puede escalar, reiniciar, borrar pods y quitar nodos.** El plugin no
90
+ > aplica ningún filtro de sólo-lectura. Si el modelo decide que la forma de arreglar lo que ve es reiniciar
91
+ > el Deployment, tiene la herramienta para hacerlo.
92
+ >
93
+ > **Recomendación: no uses `Auto` en triggers.** Marca a mano las tools de lectura que el análisis necesite.
94
+ > Es más trabajo una vez y te ahorra una sorpresa en producción.
95
+
96
+ **En el Playground** — `Auto` hace una **llamada previa** al modelo pidiéndole que elija qué tools necesita, y
97
+ sólo le pasa esas. Es más barato en tokens de contexto pero más caro en llamadas, y te muestra la elección
98
+ como `[Auto tools] selected: ...`. Sigue sin filtrar las de escritura.
99
+
100
+ ## El coste, con números
101
+
102
+ El gasto de un trigger es, aproximadamente:
103
+
104
+ ```
105
+ coste ≈ (nº de disparos) × (nº de pasos) × (tokens de contexto acumulados)
106
+ ```
107
+
108
+ Los tres factores se multiplican, y el primero es el que se dispara sin que te des cuenta:
109
+
110
+ | Trigger | Disparos al día en un clúster mediano |
111
+ |---------|----------------------------------------|
112
+ | `Deployment` + `ADDED` | decenas |
113
+ | `Deployment` + `MODIFIED` | cientos |
114
+ | `Pod` + `ADDED` | cientos |
115
+ | `Pod` + `MODIFIED` | **miles** — cada cambio de estado, cada probe, cada reinicio |
116
+ | `Pod` + *Any* | lo anterior, por tres |
117
+
118
+ Reglas prácticas:
119
+
120
+ 1. **Empieza siempre por `ADDED`**, nunca por *Any*. Analiza lo que nace, no lo que respira.
121
+ 2. **Evita `Pod`.** Analiza el `Deployment`, que es donde está la decisión de diseño; los pods sólo la
122
+ ejecutan, y hay muchos más.
123
+ 3. **`steps` bajo para empezar** (3–5). Súbelo sólo si el campo `not_visible` de los análisis te dice que al
124
+ modelo le faltó información.
125
+ 4. **Mira los tokens** en la línea de cabecera de cada análisis (`IN:`/`OUT:`). Es la medida real, no una
126
+ estimación.
127
+ 5. **Prueba en el Playground.** Un prompt mal calibrado que hace 15 llamadas a tools se detecta gratis ahí,
128
+ no en producción.
129
+
130
+ Los campos `Input / Output cost per M tokens` del diálogo de LLM existen justo para que puedas traducir esos
131
+ tokens a dinero.
@@ -0,0 +1,147 @@
1
+ # Plantillas de prompt
2
+
3
+ Pinocchio renderiza los prompts con **[nunjucks](https://mozilla.github.io/nunjucks/)** (la implementación
4
+ JavaScript de Jinja2), con `autoescape` activado. Esta página recoge qué variables hay disponibles en cada
5
+ ruta y cómo escribir un system que produzca findings útiles en vez de generalidades.
6
+
7
+ ## Qué contexto recibe la plantilla
8
+
9
+ Esto es lo que más confusión genera, así que va primero y en tabla:
10
+
11
+ | Tipo de trigger | `promptType` | Contexto de render | El `system` |
12
+ |-----------------|--------------|--------------------|-------------|
13
+ | `artifact` | `jinja` | **El objeto de Kubernetes entero** | Se usa |
14
+ | `artifact` | `artifact` | *(no hay render: el prompt es `JSON.stringify(objeto)`)* | Se usa |
15
+ | `business` | cualquiera | **Vacío `{}`** | **Se ignora** |
16
+
17
+ El caso `business` merece repetirse: aunque rellenes el campo *Spaces*, **los datos del evento no llegan a la
18
+ plantilla**. Cualquier `{{ variable }}` se renderiza como cadena vacía. Un prompt `business` es, en la
19
+ práctica, texto fijo, y su valor está en las tools que le des al modelo, no en los datos que le pases.
20
+
21
+ ## Variables disponibles en `artifact` + `jinja`
22
+
23
+ > ⚠️ **Antes de nada: el objeto es el contexto, no un adjunto.** El modelo recibe **sólo el texto
24
+ > renderizado**. Lo que la plantilla no interpole, no llega. Es el error más caro de esta página: escribes
25
+ > `Audita el Deployment {{ metadata.name }}`, el modelo recibe `Audita el Deployment api-gateway`, y te
26
+ > contesta que le pases el YAML. Si quieres que audite el recurso, **vuelca el recurso en la plantilla**
27
+ > (`{{ spec | dump(2) }}`) o usa `promptType: artifact`.
28
+
29
+ El contexto **es el objeto de Kubernetes tal cual**. No hay envoltorio ni prefijo: escribes las rutas del
30
+ manifiesto directamente.
31
+
32
+ ```jinja
33
+ {{ kind }} → "Deployment"
34
+ {{ apiVersion }} → "apps/v1"
35
+ {{ metadata.name }} → "api-gateway"
36
+ {{ metadata.namespace }} → "prod"
37
+ {{ metadata.labels.app }} → "gateway"
38
+ {{ metadata.creationTimestamp }} → "2026-09-08T10:14:21Z"
39
+ {{ spec.replicas }} → 3
40
+ {{ spec.template.spec.serviceAccountName }} → "gateway-sa"
41
+ {{ status.readyReplicas }} → 2
42
+ ```
43
+
44
+ La estructura cambia según el kind: en un `Pod` los contenedores están en `spec.containers`, mientras que en
45
+ un `Deployment` o un `StatefulSet` están en `spec.template.spec.containers`. Escribe la plantilla para el
46
+ kind concreto del trigger.
47
+
48
+ Bucles y condicionales funcionan como en Jinja2:
49
+
50
+ ```jinja
51
+ Contenedores del {{ kind }} {{ metadata.name }}:
52
+ {% for c in spec.template.spec.containers %}
53
+ - {{ c.name }} → {{ c.image }}
54
+ {% if c.securityContext %}securityContext: {{ c.securityContext | dump }}{% else %}sin securityContext{% endif %}
55
+ {% if not c.resources.limits %}⚠ sin resource limits{% endif %}
56
+ {% endfor %}
57
+ ```
58
+
59
+ El filtro `| dump` serializa un subobjeto a JSON, y acepta un argumento de indentación (`{{ spec | dump(2) }}`).
60
+ Es la forma corta de meter un bloque entero del manifiesto en el prompt sin recorrerlo campo a campo — y,
61
+ como se explica arriba, **es lo que hace que el modelo vea de verdad el recurso**.
62
+
63
+ > ⚠️ `autoescape` está **activado**. Los caracteres `<`, `>`, `&`, `'` y `"` de los valores salen escapados
64
+ > como entidades HTML. Rara vez importa en un manifiesto de Kubernetes, pero si un valor te llega como
65
+ > `&quot;` en el prompt, esta es la razón. El filtro `| safe` lo desactiva para una expresión concreta.
66
+
67
+ ## `artifact` puro: cuando no necesitas plantilla
68
+
69
+ Si lo que quieres es "toma el manifiesto entero y audítalo", no escribas plantilla: pon `promptType` en
70
+ `artifact`, deja el prompt vacío y mete toda la instrucción en el `system`. El prompt será el objeto
71
+ serializado completo.
72
+
73
+ Es más robusto que una plantilla —no se te olvida ningún campo, y funciona igual para cualquier kind— a
74
+ cambio de gastar más tokens de entrada.
75
+
76
+ ## Escribir un buen `system`
77
+
78
+ El `system` es donde de verdad se decide la calidad del análisis. Cuatro cosas que funcionan:
79
+
80
+ **1. Dale un rol y un marco de referencia concreto.**
81
+
82
+ ```
83
+ Eres un auditor de seguridad de Kubernetes. Evalúas recursos contra los Pod Security
84
+ Standards (baseline y restricted) y contra el CIS Kubernetes Benchmark.
85
+ ```
86
+
87
+ **2. Exige evidencia, y prohíbe explícitamente inventar.**
88
+
89
+ ```
90
+ Cada finding debe citar en `evidence` el fragmento literal del manifiesto que lo demuestra.
91
+ Si no puedes citar evidencia, NO emitas el finding: añádelo a `not_visible` en su lugar.
92
+ ```
93
+
94
+ Esta instrucción es la que más reduce las alucinaciones. `not_visible` existe justo para darle al modelo una
95
+ salida honesta cuando no sabe algo.
96
+
97
+ **3. Dile qué hacer con las tools, no sólo que las tiene.**
98
+
99
+ ```
100
+ Antes de concluir, usa `get_object_events` para comprobar si el recurso ya está fallando,
101
+ y `get_workload_config_refs` para ver qué ConfigMaps y Secrets consume.
102
+ ```
103
+
104
+ Un modelo con tools y sin instrucciones sobre cuándo usarlas tiende a no usarlas, o a usarlas todas.
105
+
106
+ **4. Calibra la severidad, o te llegará todo como `critical`.**
107
+
108
+ ```
109
+ critical = explotable ahora mismo para escapar del contenedor o acceder a datos de otros tenants.
110
+ high = incumple `restricted` y amplía la superficie de ataque de forma significativa.
111
+ medium = mala práctica con impacto acotado.
112
+ low = higiene, sin impacto directo de seguridad.
113
+ ```
114
+
115
+ ## Un system completo, listo para copiar
116
+
117
+ ```
118
+ Eres un auditor de seguridad de Kubernetes. Analizas un único recurso y devuelves un
119
+ informe estructurado.
120
+
121
+ REGLAS
122
+ - Evalúa contra los Pod Security Standards (baseline y restricted).
123
+ - Cada finding DEBE citar en `evidence` el fragmento literal del manifiesto que lo prueba.
124
+ - Si no puedes probar algo con evidencia, no lo afirmes: añádelo a `not_visible`.
125
+ - Lista en `controls_passed` los controles que el recurso SÍ cumple.
126
+ - Sé específico en `remediation`: el fragmento de YAML corregido, no un consejo genérico.
127
+
128
+ SEVERIDAD
129
+ - critical: explotable ahora para escapar del contenedor o alcanzar otros tenants.
130
+ - high: incumple `restricted` y amplía la superficie de ataque de forma significativa.
131
+ - medium: mala práctica con impacto acotado.
132
+ - low: higiene, sin impacto directo.
133
+
134
+ INFORME
135
+ En `report`, redacta en markdown un resumen para el equipo propietario del recurso:
136
+ qué es, qué riesgo tiene, qué hacer primero. Español, tono directo, sin relleno.
137
+ ```
138
+
139
+ ## Probar antes de activar
140
+
141
+ No afines prompts en producción. El [Playground](../user/06-playground.md) ejecuta la misma tubería contra un
142
+ payload pegado a mano y te enseña, en la pestaña **IN**, el prompt **ya renderizado** que recibió el modelo.
143
+ Es la forma más rápida de descubrir que `{{ spec.containers }}` estaba vacío porque el kind era un
144
+ `Deployment`.
145
+
146
+ Recuerda las dos diferencias del Playground al probar plantillas: en modo Business el **payload es el
147
+ prompt**, y en modo Artifact el `promptType` se deriva de si el campo Prompt está vacío o no.
@@ -0,0 +1,89 @@
1
+ # Permisos y acceso
2
+
3
+ ## Los scopes del canal
4
+
5
+ Pinocchio declara al core sólo **dos** scopes válidos para su canal, en este orden de menor a mayor:
6
+
7
+ | Scope | Nivel | Significado |
8
+ |-------|-------|-------------|
9
+ | `none` | 1 | Nivel mínimo. Suficiente para abrir el canal. |
10
+ | `cluster` | 2 | Nivel de administración. Cubre todo lo anterior. |
11
+
12
+ Una clave de acceso concede permiso sobre el canal `pinocchio` con uno de esos dos valores. El canal pide
13
+ como scope de instancia `NONE`, así que **cualquiera de los dos basta para abrirlo y usarlo entero**.
14
+
15
+ Cualquier otro literal en el scope (`view`, `restart`, `filter`, …) es **inválido para este canal**: el core
16
+ lo rechaza con un aviso muy visible en el log de autorización:
17
+
18
+ ```
19
+ ***************** Inexistent scope 'view' on channel 'pinocchio' *****************
20
+ ```
21
+
22
+ Si un usuario no consigue abrir el canal, ese log es el primer sitio donde mirar.
23
+
24
+ ## Lo que hay que entender antes de repartir claves
25
+
26
+ Pinocchio **no tiene permisos granulares**. No hay un scope para "sólo leer los findings" y otro para
27
+ "editar triggers". Quien puede abrir el canal, puede hacerlo todo. Esto es lo que "todo" incluye:
28
+
29
+ **1. Leer los análisis de todos los demás.** Los análisis se guardan en el canal, no por usuario. Cualquiera
30
+ que abra el canal recibe de golpe los últimos 50, con los manifiestos y los findings que contengan. Si un
31
+ trigger analiza recursos de un namespace sensible, esos datos son visibles para todos los que tengan acceso
32
+ al canal.
33
+
34
+ **2. Borrar los análisis de todos los demás.** El botón **Clear back** vacía la memoria del canal para todo
35
+ el mundo, sin confirmación adicional más allá del propio diálogo.
36
+
37
+ **3. Editar la configuración de IA compartida.** Los diálogos *Provider* y *LLM* escriben en el almacén
38
+ **común** de kwirth. Un usuario de Pinocchio puede añadir, modificar o **borrar** los providers y modelos que
39
+ usan los demás plugins de IA del clúster.
40
+
41
+ **4. Ver y cambiar las API keys.** El diálogo de provider tiene un botón de ojo que revela la clave, y un
42
+ botón **Export** que descarga todos los providers **con las claves en claro**.
43
+
44
+ **5. Crear triggers que ejecutan acciones de escritura.** Ver el apartado siguiente.
45
+
46
+ > **Conclusión operativa: trata el acceso al canal `pinocchio` como un permiso de administración.** No es un
47
+ > visor de logs. Concédelo al equipo que gobierna la plataforma, no a los usuarios de aplicación.
48
+
49
+ ## Las tools de escritura y el service account del backend
50
+
51
+ Este es el punto que más atención merece.
52
+
53
+ El catálogo de tools incluye acciones que **modifican el clúster**: `add_replica`, `remove_replica`,
54
+ `restart_deployment`, `delete_pod`, `add_node`, `remove_node`, `start_node`, `stop_node`. Pinocchio **no
55
+ filtra las tools de escritura**: si una versión de trigger las tiene marcadas —o tiene el interruptor
56
+ **`Auto`** activado, que entrega el catálogo entero— el modelo puede invocarlas.
57
+
58
+ Y cuando las invoca, se ejecutan con el **service account del backend de kwirth**, no con los permisos del
59
+ usuario que configuró el trigger. Un usuario con el scope mínimo puede, a través de un trigger, provocar
60
+ acciones que su propio nivel de acceso no le permitiría hacer directamente.
61
+
62
+ Tres medidas concretas:
63
+
64
+ 1. **Prohíbe `Auto` en triggers** como norma de equipo. Que las tools se marquen a mano, una a una.
65
+ 2. **Revisa los triggers como revisarías código.** El diálogo *Import / Export* permite volcarlos a JSON:
66
+ ese fichero se puede versionar y revisar en un pull request.
67
+ 3. **Ajusta el RBAC del service account del backend** a lo que de verdad necesites. Si en tu instalación
68
+ Pinocchio sólo tiene que auditar, el backend no debería poder escalar deployments ni borrar pods. Ésa es
69
+ la barrera que de verdad detiene el problema, y está fuera del plugin: es el RBAC de kwirth en el clúster.
70
+
71
+ ## Auditoría
72
+
73
+ Todas las invocaciones de tools quedan como trazas en el log del backend:
74
+
75
+ ```
76
+ [pinocchio] tool get_pod_logs {"namespace":"prod","name":"api-gateway-7d9f"}
77
+ [pinocchio] tool get_pod_logs response: {...}
78
+ ```
79
+
80
+ Además, cada análisis muestra en su cabecera qué LLM lo produjo y cuántos tokens costó, y la traza completa
81
+ de la conversación con las tools es visible en las pestañas **IN**/**OUT** del Playground cuando se ejecuta
82
+ desde allí.
83
+
84
+ ## Superficie de red
85
+
86
+ El Playground dispara sus eventos con un `POST {clusterUrl}/provider/business`, autenticado con la clave de
87
+ acceso del usuario. Es el mismo endpoint por el que los sistemas externos inyectan eventos de negocio: quien
88
+ tenga una clave válida para ese provider puede inyectar eventos y, por tanto, disparar los triggers
89
+ `business`. Ténlo en cuenta al repartir claves con acceso al provider `business`.