@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 +21 -0
- package/admin/01-setup.md +98 -0
- package/admin/02-ai-config.md +125 -0
- package/admin/03-tools.md +131 -0
- package/admin/04-prompts.md +147 -0
- package/admin/05-rbac.md +89 -0
- package/admin/06-limits.md +113 -0
- package/images/admin-ai-llm.png +0 -0
- package/images/admin-ai-provider.png +0 -0
- package/images/import-export.png +0 -0
- package/images/playground-call.png +0 -0
- package/images/playground-in.png +0 -0
- package/images/playground-llm.png +0 -0
- package/images/playground-out.png +0 -0
- package/images/tool-selector.png +0 -0
- package/images/triggers-dialog.png +0 -0
- package/images/ui-clear-dialog.png +0 -0
- package/images/ui-config-menu.png +0 -0
- package/images/ui-tab.png +0 -0
- package/index.html +64 -0
- package/index.md +42 -0
- package/kwirth-dark.css +458 -0
- package/package.json +9 -0
- package/serve.cmd +1 -0
- package/serve.mjs +84 -0
- package/user/00-how-it-works.md +88 -0
- package/user/01-introduction.md +60 -0
- package/user/02-ui-tour.md +96 -0
- package/user/03-concepts.md +160 -0
- package/user/04-triggers.md +96 -0
- package/user/05-findings.md +108 -0
- package/user/06-playground.md +134 -0
- package/user/07-import-export.md +83 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Recorrido por la UI
|
|
2
|
+
|
|
3
|
+
Pinocchio es un canal, así que se abre como cualquier otro canal de kwirth: eliges el canal **Pinocchio** en
|
|
4
|
+
el selector y lo arrancas. No tiene diálogo de *Setup* — no hay nada que configurar antes de arrancar.
|
|
5
|
+
|
|
6
|
+
## La pestaña
|
|
7
|
+
|
|
8
|
+
La pestaña es una única tarjeta con una cabecera y un flujo de mensajes que crece hacia abajo.
|
|
9
|
+
|
|
10
|
+

|
|
11
|
+
|
|
12
|
+
**Cabecera**
|
|
13
|
+
|
|
14
|
+
| Elemento | Qué es |
|
|
15
|
+
|--------------|-------------------------------------------------------------------------------|
|
|
16
|
+
| `Events: N` | Número de entradas en el flujo (análisis + mensajes). No es el número de findings. |
|
|
17
|
+
| `Status` | `started`, `paused` o `stopped`, según el estado del canal. |
|
|
18
|
+
| **Clear** | Abre el diálogo de limpieza (ver abajo). |
|
|
19
|
+
| **Playground** | Abre el banco de pruebas. Ver [El Playground](06-playground.md). |
|
|
20
|
+
| **Config** | Abre el menú de configuración. |
|
|
21
|
+
|
|
22
|
+
**Flujo**
|
|
23
|
+
|
|
24
|
+
El cuerpo hace autoscroll al final mientras estés abajo del todo; si subes a leer algo, deja de seguirte
|
|
25
|
+
hasta que vuelvas al final. Cada análisis se pinta como:
|
|
26
|
+
|
|
27
|
+
1. Una línea de cabecera con la marca de tiempo, el evento (`ADDED Deployment 'nginx' in namespace 'demo'`),
|
|
28
|
+
el LLM usado y los tokens consumidos, más un botón **Report**.
|
|
29
|
+
2. Los **findings**, ordenados de `critical` a `low`, cada uno con su etiqueta de severidad.
|
|
30
|
+
3. Una **tira de resumen** con PSS, el recuento por severidad y el riesgo global.
|
|
31
|
+
|
|
32
|
+
Los findings y la tira de resumen son **clicables** y abren su diálogo de detalle.
|
|
33
|
+
|
|
34
|
+
## El menú Config
|
|
35
|
+
|
|
36
|
+
El botón **Config** abre un menú de tres entradas, la primera de ellas un grupo desplegable. El orden no es
|
|
37
|
+
casual: cada una depende de la anterior.
|
|
38
|
+
|
|
39
|
+

|
|
40
|
+
|
|
41
|
+
| Entrada | Se habilita cuando… | Qué configura |
|
|
42
|
+
|-------------------|----------------------------------------|--------------------------------------------------|
|
|
43
|
+
| **AI** ▸ **AI providers** | siempre | Proveedores de IA (OpenAI, Google, …) y su API key |
|
|
44
|
+
| **AI** ▸ **AI models** | hay al menos un provider | Modelos concretos, con temperatura y coste |
|
|
45
|
+
| **Trigger** | hay al menos un LLM | Los triggers y sus versiones |
|
|
46
|
+
| **Import / Export** | siempre | Volcado y carga de triggers en JSON |
|
|
47
|
+
|
|
48
|
+
**AI** es un grupo plegable —el patrón habitual de kwirth, el mismo que usan Excubitor y Agora— y **arranca
|
|
49
|
+
plegado**: hay que clicarlo para ver *AI providers* y *AI models*. Es lo que menos se toca una vez montado,
|
|
50
|
+
por eso está recogido.
|
|
51
|
+
|
|
52
|
+
Si abres Pinocchio por primera vez y **Trigger** está en gris, no es un fallo: es que aún no hay un LLM
|
|
53
|
+
configurado. Empieza por **AI ▸ AI providers**. El detalle está en
|
|
54
|
+
[Providers y modelos de IA](../admin/02-ai-config.md).
|
|
55
|
+
|
|
56
|
+
> Los diálogos de **AI providers** y **AI models** no son de Pinocchio: son los diálogos comunes de IA de
|
|
57
|
+
> kwirth (`AiConfigProvider` / `AiConfigLlm`), los mismos que usan los demás plugins con IA. Lo que configures
|
|
58
|
+
> aquí lo verán también ellos.
|
|
59
|
+
|
|
60
|
+
## Los diálogos de detalle
|
|
61
|
+
|
|
62
|
+
**Detalle de un finding** (clic en cualquier finding) — muestra, en dos columnas, todo lo que el modelo
|
|
63
|
+
rellenó: Control ID, categoría, confianza, *risk score*, descripción, evidencia e impacto a la izquierda;
|
|
64
|
+
remediación y referencias a la derecha. Los campos vacíos no se pintan.
|
|
65
|
+
|
|
66
|
+
**Detalle del análisis** (clic en la tira de resumen) — el recurso analizado (kind, nombre, namespace,
|
|
67
|
+
imágenes), PSS actual y objetivo, riesgo global y recuento por severidad a la izquierda; controles superados,
|
|
68
|
+
lo que el modelo **no pudo ver** y los próximos pasos a la derecha.
|
|
69
|
+
|
|
70
|
+
**Report** (botón *Report* de la cabecera del análisis) — abre el informe en markdown renderizado. El botón
|
|
71
|
+
está deshabilitado si ese análisis no trajo informe.
|
|
72
|
+
|
|
73
|
+
## El diálogo Clear
|
|
74
|
+
|
|
75
|
+
Hay dos limpiezas distintas y conviene no confundirlas, porque una es local y la otra afecta a todo el mundo:
|
|
76
|
+
|
|
77
|
+

|
|
78
|
+
|
|
79
|
+
- **Clear my view** — vacía **tu** pantalla. El backend conserva sus análisis y te los reenviará si
|
|
80
|
+
reconectas.
|
|
81
|
+
- **Clear back** — borra los análisis guardados **en el canal**. Afecta a todos los fronts conectados y no
|
|
82
|
+
tiene vuelta atrás.
|
|
83
|
+
|
|
84
|
+
## Colores de severidad
|
|
85
|
+
|
|
86
|
+
Los mismos en toda la UI:
|
|
87
|
+
|
|
88
|
+
| Nivel | Color |
|
|
89
|
+
|------------|----------|
|
|
90
|
+
| `critical` | rojo |
|
|
91
|
+
| `high` | naranja |
|
|
92
|
+
| `medium` | verde |
|
|
93
|
+
| `low` | gris |
|
|
94
|
+
|
|
95
|
+
> ⚠️ Sí: `medium` se pinta en **verde**. Es el comportamiento actual del plugin y sorprende la primera vez.
|
|
96
|
+
> Guíate por el texto de la etiqueta, no por el color.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Triggers, versiones y prompts
|
|
2
|
+
|
|
3
|
+
Toda la configuración de Pinocchio se reduce a una lista de **triggers**. Esta página explica el modelo de
|
|
4
|
+
datos y, sobre todo, **qué hace realmente el backend con cada campo**, que no siempre es lo que el nombre
|
|
5
|
+
sugiere.
|
|
6
|
+
|
|
7
|
+
## Trigger
|
|
8
|
+
|
|
9
|
+
Un trigger define **cuándo** se dispara el análisis:
|
|
10
|
+
|
|
11
|
+
| Campo | Aplica a | Qué hace |
|
|
12
|
+
|-------------|-------------|--------------------------------------------------------------------------------|
|
|
13
|
+
| `id` | ambos | Identificador libre. Es lo que ves en la lista y lo que se usa al importar/exportar. |
|
|
14
|
+
| `trigger` | ambos | `artifact` (objeto de Kubernetes) o `business` (JSON externo). |
|
|
15
|
+
| `kind` | `artifact` | El kind de Kubernetes a vigilar. Debe coincidir **exactamente** con `obj.kind`. |
|
|
16
|
+
| `k8sEvent` | `artifact` | `ADDED`, `MODIFIED`, `DELETED` — o vacío (*Any*) para no filtrar por evento. |
|
|
17
|
+
| `versions` | ambos | Lista de versiones. Ver abajo. |
|
|
18
|
+
|
|
19
|
+
El matching de un evento de Kubernetes es literal:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
trigger.trigger === 'artifact'
|
|
23
|
+
&& trigger.kind === evento.obj.kind
|
|
24
|
+
&& (!trigger.k8sEvent || trigger.k8sEvent === evento.type)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Los triggers de tipo `business` **no filtran por espacio ni por tipo en el matching**: cualquier evento de
|
|
28
|
+
negocio que llegue al canal dispara **todos** los triggers `business` que tengan una versión activa. El campo
|
|
29
|
+
*Spaces* de la versión no se usa para filtrar (ver más abajo).
|
|
30
|
+
|
|
31
|
+
## Versión
|
|
32
|
+
|
|
33
|
+
Una versión es **el contenido del análisis**: qué se le pregunta al modelo y con qué medios.
|
|
34
|
+
|
|
35
|
+
| Campo | Qué hace |
|
|
36
|
+
|---------------|---------------------------------------------------------------------------------------------|
|
|
37
|
+
| `id` | Identificador dentro del trigger. Único por trigger. |
|
|
38
|
+
| `description` | Texto libre; se ve bajo el id en la lista de versiones. |
|
|
39
|
+
| `enabled` | Si está activa. **Sólo una versión por trigger puede estar activa a la vez.** |
|
|
40
|
+
| `llm` | El id del LLM configurado que se va a usar. |
|
|
41
|
+
| `system` | El *system prompt*. ⚠️ **Se ignora en los triggers `business`** (ver abajo). |
|
|
42
|
+
| `promptType` | `jinja` o `artifact`. Determina cómo se construye el prompt. |
|
|
43
|
+
| `prompt` | La plantilla del prompt. Deshabilitada cuando `promptType` es `artifact`. |
|
|
44
|
+
| `steps` | Máximo de pasos del agente. Si es 0 o vacío, el backend usa **15**. |
|
|
45
|
+
| `tools` | Lista de tools que el modelo puede llamar. |
|
|
46
|
+
| `autoTools` | Si está marcado, se le entrega **todo el catálogo** de tools. |
|
|
47
|
+
| `spaces` | Lista `space.type`. Hoy **no filtra nada**; ver [Límites](../admin/06-limits.md). |
|
|
48
|
+
| `action` | `inform` / `cancel` / `repair`. **Hoy el backend no lo lee**: todo se comporta como `inform`. |
|
|
49
|
+
|
|
50
|
+
### Por qué "versiones" y no simplemente "triggers"
|
|
51
|
+
|
|
52
|
+
Porque afinar un prompt es un proceso iterativo y **quieres conservar el que funcionaba**. El patrón previsto
|
|
53
|
+
es: clonas la versión activa, retocas el prompt en la copia, la activas (lo que desactiva la anterior
|
|
54
|
+
automáticamente) y comparas resultados. Si empeora, reactivas la vieja con un clic del interruptor.
|
|
55
|
+
|
|
56
|
+
Al clonar una versión, la copia nace **desactivada** a propósito, para que no te cambie el comportamiento sin
|
|
57
|
+
querer.
|
|
58
|
+
|
|
59
|
+
## Cómo se construye el prompt
|
|
60
|
+
|
|
61
|
+
Este es el punto donde más gente se equivoca. Depende de la combinación tipo de trigger × `promptType`:
|
|
62
|
+
|
|
63
|
+
### `artifact` + `promptType: jinja`
|
|
64
|
+
|
|
65
|
+
El `prompt` se renderiza con **nunjucks** usando el objeto de Kubernetes como contexto:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
prompt = nunjucks.renderString(version.prompt, evento.obj)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Es decir, las variables disponibles son los campos del propio objeto:
|
|
72
|
+
|
|
73
|
+
```jinja
|
|
74
|
+
Audita este {{ kind }} llamado {{ metadata.name }} del namespace {{ metadata.namespace }}.
|
|
75
|
+
|
|
76
|
+
Imágenes:
|
|
77
|
+
{% for c in spec.template.spec.containers %}- {{ c.image }}
|
|
78
|
+
{% endfor %}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
El `system` **sí se usa**. Si está vacío, el backend cae a `'You are a very polite AI system'`.
|
|
82
|
+
|
|
83
|
+
> ⚠️ **El objeto es el contexto de la plantilla, no un adjunto.** El modelo recibe **únicamente el texto
|
|
84
|
+
> renderizado**: lo que tu plantilla no interpole, el modelo no lo ve. Un prompt como
|
|
85
|
+
> `Audita el Deployment {{ metadata.name }} del namespace {{ metadata.namespace }}.` le llega al modelo como
|
|
86
|
+
> `Audita el Deployment api-gateway del namespace demo.` — sin manifiesto, sin imágenes, sin
|
|
87
|
+
> `securityContext`. Y el modelo contesta lo único que puede contestar:
|
|
88
|
+
>
|
|
89
|
+
> 
|
|
90
|
+
>
|
|
91
|
+
> Si quieres que audite el recurso, **mete el recurso en la plantilla** (`{{ spec | dump(2) }}`) o usa
|
|
92
|
+
> `promptType: artifact`, que le pasa el objeto entero.
|
|
93
|
+
|
|
94
|
+
### `artifact` + `promptType: artifact`
|
|
95
|
+
|
|
96
|
+
El prompt es, literalmente, **el objeto entero serializado**:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
prompt = JSON.stringify(evento.obj)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
El campo `prompt` se ignora por completo (y la UI lo deshabilita). Toda la instrucción tiene que estar en el
|
|
103
|
+
`system`. Es la opción cómoda cuando el system ya dice "eres un auditor de PSS, analiza el manifiesto que te
|
|
104
|
+
paso".
|
|
105
|
+
|
|
106
|
+
### `business` (cualquier `promptType`)
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
prompt = nunjucks.renderString(version.prompt, {})
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
⚠️ Dos cosas importantes, ambas comprobables en el código del backend:
|
|
113
|
+
|
|
114
|
+
1. **El contexto de renderizado está vacío.** Aunque pongas espacios en el campo *Spaces*, los datos del
|
|
115
|
+
evento **no se pasan a la plantilla**. Cualquier `{{ variable }}` que escribas se renderiza como cadena
|
|
116
|
+
vacía. En la práctica, un prompt `business` es texto fijo.
|
|
117
|
+
2. **El `system` de la versión se ignora.** El backend usa un system fijo:
|
|
118
|
+
*"Use the tools provided to find information, and once you have the data, format your final response
|
|
119
|
+
strictly as a JSON object according to the schema."*
|
|
120
|
+
|
|
121
|
+
Por eso un trigger `business` sólo tiene sentido si su valor está en las **tools**: el prompt fijo plantea la
|
|
122
|
+
pregunta ("¿está el clúster saturado?") y el modelo la resuelve consultando el clúster.
|
|
123
|
+
|
|
124
|
+
Ambos comportamientos están recogidos en [Límites conocidos](../admin/06-limits.md).
|
|
125
|
+
|
|
126
|
+
## Qué devuelve cada tipo
|
|
127
|
+
|
|
128
|
+
| Tipo | Esquema de salida |
|
|
129
|
+
|------------|--------------------------------------------------------------------------------------------------------|
|
|
130
|
+
| `artifact` | Objeto completo: `resource`, `pss_current`, `pss_target`, `score_summary`, `global_risk`, `controls_passed`, `not_visible`, `next_steps`, `report`, `findings[]`, `hardened_yaml`. |
|
|
131
|
+
| `business` | Sólo `{ response: string }`, que se muestra como un mensaje de texto en la pestaña. |
|
|
132
|
+
|
|
133
|
+
El detalle de cada campo está en [Leer los findings](05-findings.md).
|
|
134
|
+
|
|
135
|
+
## Un trigger de ejemplo, entero
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"id": "pss-deployments",
|
|
140
|
+
"trigger": "artifact",
|
|
141
|
+
"kind": "Deployment",
|
|
142
|
+
"k8sEvent": "ADDED",
|
|
143
|
+
"versions": [
|
|
144
|
+
{
|
|
145
|
+
"id": "v2",
|
|
146
|
+
"description": "PSS restricted + contexto de eventos",
|
|
147
|
+
"enabled": true,
|
|
148
|
+
"llm": "gemini-flash",
|
|
149
|
+
"promptType": "jinja",
|
|
150
|
+
"system": "Eres un auditor de seguridad de Kubernetes. Evalúa contra los Pod Security Standards y devuelve findings accionables.",
|
|
151
|
+
"prompt": "Audita el Deployment {{ metadata.name }} en {{ metadata.namespace }}.\n\nManifiesto:\n{{ spec | dump(2) }}",
|
|
152
|
+
"action": "inform",
|
|
153
|
+
"steps": 8,
|
|
154
|
+
"tools": ["get_deployment_yaml", "get_object_events", "get_workload_config_refs"],
|
|
155
|
+
"autoTools": false,
|
|
156
|
+
"spaces": []
|
|
157
|
+
}
|
|
158
|
+
]
|
|
159
|
+
}
|
|
160
|
+
```
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Configurar triggers
|
|
2
|
+
|
|
3
|
+
**Config → Trigger** abre el editor. Es un diálogo a dos paneles: a la izquierda la lista de triggers y, bajo
|
|
4
|
+
ella, las versiones del trigger seleccionado; a la derecha el editor de la versión.
|
|
5
|
+
|
|
6
|
+

|
|
7
|
+
|
|
8
|
+
## Panel izquierdo
|
|
9
|
+
|
|
10
|
+
**Triggers.** Cada fila muestra el id y, debajo, el tipo y el kind (`artifact · Deployment`). Al final de la
|
|
11
|
+
fila hay dos iconos que aparecen al pasar el ratón: **clonar** y **borrar**.
|
|
12
|
+
|
|
13
|
+
Para crear uno, escribe un id en la caja de abajo y pulsa **+** (o Enter). Si la dejas vacía, se genera
|
|
14
|
+
`trigger-N`. El trigger nace como `artifact` sin kind y sin versiones.
|
|
15
|
+
|
|
16
|
+
Clonar un trigger copia **todo**, versiones incluidas, con el id sufijado `-copy`. Borrar pide confirmación.
|
|
17
|
+
|
|
18
|
+
**Versions.** Debajo, las versiones del trigger seleccionado. Cada una tiene un **interruptor**:
|
|
19
|
+
|
|
20
|
+
> ⚠️ Los interruptores son **excluyentes**. Al activar una versión, el resto se desactivan solas. Es
|
|
21
|
+
> intencionado: un trigger sólo puede tener un comportamiento activo.
|
|
22
|
+
|
|
23
|
+
Al clonar una versión, la copia nace **desactivada**, para que clonar nunca cambie lo que está pasando en
|
|
24
|
+
producción.
|
|
25
|
+
|
|
26
|
+
## Panel derecho: el editor de la versión
|
|
27
|
+
|
|
28
|
+
Los campos de la fila superior se dividen en dos grupos y **no se guardan igual**:
|
|
29
|
+
|
|
30
|
+
**Campos del trigger** — `Trigger ID`, `Trigger type`, `Kind`, `K8s Event`. Se aplican **según los tocas**,
|
|
31
|
+
sin necesidad de pulsar nada más.
|
|
32
|
+
|
|
33
|
+
**Campos de la versión** — todo lo demás. Sólo se guardan al pulsar **Add** / **Update**.
|
|
34
|
+
|
|
35
|
+
| Campo | Notas |
|
|
36
|
+
|---------------|----------------------------------------------------------------------------------------------|
|
|
37
|
+
| `Kind` | Sólo si el tipo es `artifact`. Lista cerrada con los kinds que escucha el canal. |
|
|
38
|
+
| `K8s Event` | Sólo si el tipo es `artifact`. *Any* deja pasar `ADDED`, `MODIFIED` y `DELETED`. |
|
|
39
|
+
| `Spaces` | Sólo si el tipo es `business`. Formato `space.type,space.type`. Hoy no filtra nada. |
|
|
40
|
+
| `Version ID` | Obligatorio: el botón de guardar está deshabilitado sin él. No puede repetirse en el trigger. |
|
|
41
|
+
| `Description` | Texto libre que aparece bajo el id en la lista. |
|
|
42
|
+
| `Action` | `inform` / `cancel` / `repair`. Hoy sólo se comporta como `inform`. |
|
|
43
|
+
| `LLM` | Uno de los LLMs configurados. Sin LLMs no llegas ni a abrir este diálogo. |
|
|
44
|
+
| `Steps` | Máximo de pasos del agente. **0 o vacío ⇒ el backend usa 15**, no 0. |
|
|
45
|
+
| Tool selector | Las tools disponibles, con la casilla *auto*. Ver [Tools y pasos](../admin/03-tools.md). |
|
|
46
|
+
| `System` | El system prompt. Ignorado en triggers `business`. |
|
|
47
|
+
| `Prompt` | La plantilla. Deshabilitada cuando el tipo de prompt es `artifact`. |
|
|
48
|
+
|
|
49
|
+
## Botones
|
|
50
|
+
|
|
51
|
+
| Botón | Qué hace |
|
|
52
|
+
|------------|----------------------------------------------------------------------------|
|
|
53
|
+
| **New** | Vacía el editor para escribir una versión nueva. |
|
|
54
|
+
| **Clone** | Duplica la versión seleccionada, desactivada, con el id sufijado `-copy`. |
|
|
55
|
+
| **Remove** | Borra la versión seleccionada, con confirmación. |
|
|
56
|
+
| **Add** / **Update** | Guarda. Dice *Add* si estás creando y *Update* si estás editando. |
|
|
57
|
+
| **OK** | Cierra el diálogo y **envía la configuración al backend**. |
|
|
58
|
+
| **Cancel** | Descarta **todo** lo hecho en el diálogo. |
|
|
59
|
+
|
|
60
|
+
> ⚠️ **Add/Update no persiste nada por sí solo.** Trabajas sobre una copia local de la configuración; hasta
|
|
61
|
+
> que no pulses **OK**, el backend no se entera. Si haces cinco cambios y cierras con **Cancel**, pierdes los
|
|
62
|
+
> cinco.
|
|
63
|
+
|
|
64
|
+
## Receta: auditar cada Deployment nuevo
|
|
65
|
+
|
|
66
|
+
1. **Config → Trigger**, escribe `pss-deployments` en la caja de la izquierda y pulsa **+**.
|
|
67
|
+
2. Con el trigger seleccionado: `Trigger type` = `artifact`, `Kind` = `Deployment`, `K8s Event` = `ADDED`.
|
|
68
|
+
3. `Version ID` = `v1`. `LLM` = el que tengas. `Steps` = 5.
|
|
69
|
+
4. Tools: marca `get_deployment_yaml` y `get_object_events`.
|
|
70
|
+
5. `System`:
|
|
71
|
+
```
|
|
72
|
+
Eres un auditor de seguridad de Kubernetes. Evalúa el recurso contra los Pod Security
|
|
73
|
+
Standards (baseline y restricted). Devuelve findings accionables, con evidencia concreta
|
|
74
|
+
tomada del manifiesto. No inventes controles que no puedas justificar.
|
|
75
|
+
```
|
|
76
|
+
6. Tipo de prompt `jinja`, `Prompt`:
|
|
77
|
+
```jinja
|
|
78
|
+
Audita el Deployment {{ metadata.name }} del namespace {{ metadata.namespace }}.
|
|
79
|
+
|
|
80
|
+
Manifiesto:
|
|
81
|
+
{{ spec | dump(2) }}
|
|
82
|
+
```
|
|
83
|
+
⚠️ Las dos últimas líneas no son decorativas: **son las que le pasan el recurso al modelo**. Sin ellas
|
|
84
|
+
el modelo sólo recibe el nombre y el namespace, y te contestará pidiéndote el manifiesto. Ver
|
|
85
|
+
[Triggers, versiones y prompts](03-concepts.md).
|
|
86
|
+
7. **Add** → activa el interruptor de `v1` → **OK**.
|
|
87
|
+
8. Despliega algo y míralo aparecer en la pestaña.
|
|
88
|
+
|
|
89
|
+
> Antes de activar un trigger sobre un kind con mucho movimiento (`Pod` + `MODIFIED` es el caso extremo),
|
|
90
|
+
> pásate por [Tools y pasos del agente](../admin/03-tools.md): ahí está la cuenta de lo que eso cuesta.
|
|
91
|
+
|
|
92
|
+
## Cómo probar sin activar nada
|
|
93
|
+
|
|
94
|
+
No hace falta desplegar recursos de mentira para afinar un prompt. El
|
|
95
|
+
[Playground](06-playground.md) ejecuta exactamente la misma tubería contra un payload que pegas a mano, y
|
|
96
|
+
cuando el resultado te convence lo exporta a un trigger real.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Leer los findings
|
|
2
|
+
|
|
3
|
+
Un análisis de tipo `artifact` no devuelve texto libre: devuelve un **objeto con un esquema fijo** que el
|
|
4
|
+
modelo está obligado a rellenar. Esta página explica qué significa cada campo y —más importante— **cuánto
|
|
5
|
+
te puedes fiar de él**.
|
|
6
|
+
|
|
7
|
+
## La línea de cabecera
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
2026-09-08T10:14:22.145Z ADDED Deployment 'api-gateway' in namespace 'prod'
|
|
11
|
+
[LLM:google/gemini-2.0-flash, IN:4821, OUT:1103]
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
- El evento que disparó el análisis.
|
|
15
|
+
- El **provider y el modelo** que lo produjeron. Útil cuando comparas versiones de un trigger.
|
|
16
|
+
- **IN / OUT** — tokens de entrada y de salida. Es tu factura. Si un trigger te sorprende en el coste, aquí
|
|
17
|
+
está la respuesta.
|
|
18
|
+
|
|
19
|
+
## Los findings
|
|
20
|
+
|
|
21
|
+
Cada finding es un hallazgo concreto. En la lista sólo se ven la severidad y la descripción; el resto está a
|
|
22
|
+
un clic.
|
|
23
|
+
|
|
24
|
+
| Campo | Valores | Qué es |
|
|
25
|
+
|----------------|-----------------------------------------------------------------------------------------|--------|
|
|
26
|
+
| `level` | `critical` · `high` · `medium` · `low` | Severidad. Ordena la lista. |
|
|
27
|
+
| `control_id` | texto | Identificador del control que se incumple. |
|
|
28
|
+
| `control_name` | texto | Nombre legible del control. Es el título del diálogo de detalle. |
|
|
29
|
+
| `category` | `privileges` · `identity` · `network` · `filesystem` · `supply_chain` · `resources` · `secrets` · `general` · `platform` | Familia del hallazgo. |
|
|
30
|
+
| `confidence` | `low` · `medium` · `high` | Cuánta seguridad dice tener **el modelo**. |
|
|
31
|
+
| `evidence` | texto | El fragmento concreto que lo demuestra. |
|
|
32
|
+
| `impact` | texto | Qué puede pasar si no se arregla. |
|
|
33
|
+
| `remediation` | texto | Cómo arreglarlo. |
|
|
34
|
+
| `references` | lista | Enlaces o identificadores de referencia. |
|
|
35
|
+
| `risk_score` | número | Puntuación numérica que asigna el modelo. |
|
|
36
|
+
|
|
37
|
+
> **El campo que hay que mirar siempre es `evidence`.** Un finding con evidencia concreta ("el contenedor
|
|
38
|
+
> `api` declara `securityContext.privileged: true`") es verificable en dos segundos. Un finding sin
|
|
39
|
+
> evidencia, o con evidencia genérica, es una alucinación candidata. Cruza `confidence` con `evidence`
|
|
40
|
+
> antes de abrir un ticket.
|
|
41
|
+
|
|
42
|
+
En la lista, si la descripción contiene un fragmento entre ` **` y `** `, se pinta en negrita y subrayado.
|
|
43
|
+
Es un pequeño formateo heredado; no es markdown completo.
|
|
44
|
+
|
|
45
|
+
## La tira de resumen
|
|
46
|
+
|
|
47
|
+
Bajo los findings aparece una tira con el resumen del análisis. Se puede clicar para ver el detalle completo.
|
|
48
|
+
|
|
49
|
+
**PSS: `baseline` → `restricted`**
|
|
50
|
+
|
|
51
|
+
Los [Pod Security Standards](https://kubernetes.io/docs/concepts/security/pod-security-standards/) de
|
|
52
|
+
Kubernetes. `pss_current` es el nivel que el modelo considera que cumple el recurso **tal como está**;
|
|
53
|
+
`pss_target` es al que podría llegar aplicando las remediaciones. Los valores posibles son `privileged`,
|
|
54
|
+
`baseline`, `restricted` y `undefined`.
|
|
55
|
+
|
|
56
|
+
`privileged` es el nivel **más permisivo** (sin restricciones) y `restricted` el **más estricto**. Si ves
|
|
57
|
+
`pss_current: privileged`, es una mala noticia, no una buena.
|
|
58
|
+
|
|
59
|
+
**critical:0 high:2 medium:5 low:1**
|
|
60
|
+
|
|
61
|
+
El `score_summary`: recuento de findings por severidad, tal como lo cuenta el modelo.
|
|
62
|
+
|
|
63
|
+
**Riesgo global**
|
|
64
|
+
|
|
65
|
+
`global_risk`, la valoración de conjunto: `low`, `medium`, `high` o `critical`.
|
|
66
|
+
|
|
67
|
+
## El detalle del análisis
|
|
68
|
+
|
|
69
|
+
Al clicar la tira se abren tres cosas que no caben en la línea de resumen:
|
|
70
|
+
|
|
71
|
+
- **Controls passed** — los controles que el recurso **sí** cumple. Sirve para ver que el modelo ha hecho el
|
|
72
|
+
repaso completo y no sólo ha buscado fallos.
|
|
73
|
+
- **Not visible** — los controles que el modelo **no ha podido evaluar** con la información que tenía. Este
|
|
74
|
+
campo es oro: te dice dónde el análisis es incompleto y qué tool deberías haberle dado. Si `not_visible`
|
|
75
|
+
está lleno, sube los `steps` o añade tools.
|
|
76
|
+
- **Next steps** — la lista de acciones recomendadas, en orden.
|
|
77
|
+
|
|
78
|
+
También muestra el **recurso** analizado (kind, nombre, namespace e imágenes) tal como lo identificó el
|
|
79
|
+
modelo. Compáralo con la línea de cabecera: si no coinciden, el modelo se ha confundido de objeto y el
|
|
80
|
+
análisis entero es sospechoso.
|
|
81
|
+
|
|
82
|
+
## El informe
|
|
83
|
+
|
|
84
|
+
El botón **Report** abre el campo `report`: un informe **en markdown** redactado por el modelo, pensado para
|
|
85
|
+
leerse como documento y no como lista. Se renderiza con el visor de markdown de kwirth.
|
|
86
|
+
|
|
87
|
+
Es la salida que puedes pegar en un ticket o en un correo. Los findings son para triar; el informe es para
|
|
88
|
+
explicar.
|
|
89
|
+
|
|
90
|
+
## Cuando algo falla
|
|
91
|
+
|
|
92
|
+
Si la llamada al modelo revienta, Pinocchio **no se calla**: publica un análisis con dos findings de nivel
|
|
93
|
+
`critical`, uno con el mensaje de error legible y otro con el error serializado entero. Verás algo como:
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
critical Pinocchio analysis ended in error while processing 'events' when
|
|
97
|
+
analyzing 'api-gateway' in namespace 'prod' [Kind:Deployment]
|
|
98
|
+
critical {"name":"AI_APICallError","message":"...
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Las causas más habituales: API key caducada, cuota agotada en el proveedor, o el modelo elegido no soporta
|
|
102
|
+
salida estructurada. El segundo finding, aunque sea feo, suele traer el mensaje exacto del proveedor.
|
|
103
|
+
|
|
104
|
+
## Análisis de triggers `business`
|
|
105
|
+
|
|
106
|
+
Los triggers de negocio **no producen findings**. Su salida es un único campo de texto que aparece en el
|
|
107
|
+
flujo como un mensaje normal, precedido de otro mensaje con el evento recibido. Nada de PSS, ni severidades,
|
|
108
|
+
ni informe.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# El Playground
|
|
2
|
+
|
|
3
|
+
El Playground es el banco de pruebas: te deja **ejecutar la tubería completa** —prompt, modelo, tools— contra
|
|
4
|
+
un payload que pegas a mano, sin crear ningún trigger y sin tocar el clúster. Es donde se afinan los prompts.
|
|
5
|
+
|
|
6
|
+
Se abre con el botón **Playground** de la cabecera de la pestaña.
|
|
7
|
+
|
|
8
|
+
## El ciclo de trabajo
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
Pestaña LLM Pestaña Call Pestaña IN / OUT
|
|
12
|
+
----------- ------------ ----------------
|
|
13
|
+
elige modelo --> escribe system --> Apply Config --> Fire --> mira la traza
|
|
14
|
+
pega el payload y prompt, tools (sube al back) (inyecta
|
|
15
|
+
el evento)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Dos botones, en este orden obligatorio:
|
|
19
|
+
|
|
20
|
+
1. **Apply Config** — sube al backend el modelo, los pasos, las tools, el system y el prompt. Mientras no lo
|
|
21
|
+
pulses, **Fire** está deshabilitado. El botón se pone verde y dice *Config applied*.
|
|
22
|
+
2. **Fire** — inyecta el evento. Cualquier cambio en el modelo, los pasos, las tools, el system o el prompt
|
|
23
|
+
**vuelve a marcar la config como sucia** y tendrás que aplicarla otra vez.
|
|
24
|
+
|
|
25
|
+
## Pestaña LLM — el evento de entrada
|
|
26
|
+
|
|
27
|
+
| Campo | Qué es |
|
|
28
|
+
|---------------|-----------------------------------------------------------------------|
|
|
29
|
+
| `LLM` | Uno de los LLMs configurados. |
|
|
30
|
+
| `Max steps` | Tope de pasos del agente para esta prueba. |
|
|
31
|
+
| Business / Artifact | El tipo de evento que vas a simular. |
|
|
32
|
+
| `Artifact Kind` | Sólo en modo Artifact. Se inyecta como `kind` si el payload no lo trae. |
|
|
33
|
+
| `K8s Event` | Sólo en modo Artifact. ⚠️ Ver la advertencia de más abajo. |
|
|
34
|
+
| `Space` / `Type` | Sólo en modo Business. ⚠️ Ver la advertencia de más abajo. |
|
|
35
|
+
| El textarea grande | El payload: el JSON del artefacto, o el evento de negocio. |
|
|
36
|
+
|
|
37
|
+

|
|
38
|
+
|
|
39
|
+
El iconito de reloj junto a las cajas abre el **historial**: los últimos 25 valores que has usado en ese
|
|
40
|
+
campo, con una papelera para borrar los que sobren. Se guardan al pulsar **Save** y sobreviven al cierre del
|
|
41
|
+
diálogo.
|
|
42
|
+
|
|
43
|
+
## Pestaña Call — la llamada
|
|
44
|
+
|
|
45
|
+
`Prompt type`, el selector de tools, y los dos textareas de `System` y `Prompt`, con sus historiales. Es el
|
|
46
|
+
mismo juego de campos que el editor de triggers, más los dos botones **Apply Config** y **Fire**.
|
|
47
|
+
|
|
48
|
+

|
|
49
|
+
|
|
50
|
+
## Pestañas IN y OUT
|
|
51
|
+
|
|
52
|
+
El Playground **no reutiliza el flujo de la pestaña principal**: cuenta sólo los mensajes producidos desde
|
|
53
|
+
que lo abriste, y los reparte en dos vistas.
|
|
54
|
+
|
|
55
|
+
- **IN** — la traza de entrada: qué recibió el backend y qué le mandó al modelo. Verás
|
|
56
|
+
`[Playground] type/llm/system/prompt/tools`, cada `[Tool call]` con sus argumentos y cada `[Tool result]`
|
|
57
|
+
con lo que devolvió. Es la vista que usas para entender **por qué** el modelo contestó lo que contestó.
|
|
58
|
+
|
|
59
|
+

|
|
60
|
+
|
|
61
|
+
Fíjate en la línea `prompt:`: es la plantilla **ya renderizada**. Ahí se ve que `{{ metadata.name }}` se
|
|
62
|
+
convirtió en `api-gateway`… y también que el manifiesto **no** viaja con ella. Es la comprobación más
|
|
63
|
+
rápida de que tu plantilla dice lo que crees que dice.
|
|
64
|
+
|
|
65
|
+
- **OUT** — la respuesta: `[Playground] response` y el recuento de tokens y pasos.
|
|
66
|
+
|
|
67
|
+

|
|
68
|
+
|
|
69
|
+
Al cerrar el diálogo, estos mensajes **se retiran del flujo principal**: no ensucian la pestaña.
|
|
70
|
+
|
|
71
|
+
## Advertencias importantes
|
|
72
|
+
|
|
73
|
+
El Playground no ejecuta exactamente el mismo camino que un trigger real. Estas cuatro diferencias son las
|
|
74
|
+
que más tiempo hacen perder:
|
|
75
|
+
|
|
76
|
+
### 1. En modo Business, el payload ES el prompt
|
|
77
|
+
|
|
78
|
+
En modo Business el backend **descarta el `Prompt` de la pestaña Call** y usa el contenido del textarea de
|
|
79
|
+
evento como prompt, renderizado con jinja sobre un contexto vacío. Si estás probando un trigger de negocio,
|
|
80
|
+
escribe la pregunta en el textarea del evento, no en el campo Prompt.
|
|
81
|
+
|
|
82
|
+
### 2. En modo Business, cambiar Space/Type rompe la prueba
|
|
83
|
+
|
|
84
|
+
El backend sólo redirige un evento al Playground si llega con **`space: launch` y `type: immediate`** —los
|
|
85
|
+
valores por defecto—. Si los cambias, el evento deja de ir al Playground y pasa a evaluarse contra tus
|
|
86
|
+
triggers de negocio **reales**. Y si además el par que has escrito no es uno de los tres a los que el canal
|
|
87
|
+
está suscrito (`customers.status`, `branches.status`, `launch.immediate`), no llegará a ninguna parte y no
|
|
88
|
+
verás absolutamente nada.
|
|
89
|
+
|
|
90
|
+
En modo Artifact el problema no existe: el `Fire` usa `launch`/`immediate` sin preguntar.
|
|
91
|
+
|
|
92
|
+
### 3. El `Prompt type` que elijas no siempre manda
|
|
93
|
+
|
|
94
|
+
En modo Artifact, el backend decide el tipo de prompt **según tengas o no texto en el campo Prompt**: si hay
|
|
95
|
+
prompt lo trata como `jinja`, y si está vacío como `artifact`. El selector `Prompt type` de la pestaña Call
|
|
96
|
+
no se respeta en esta ruta. Para probar el modo `artifact` puro, **vacía el campo Prompt**.
|
|
97
|
+
|
|
98
|
+
### 4. El evento simulado siempre es `ADDED`
|
|
99
|
+
|
|
100
|
+
El objeto que inyectas se le entrega al modelo como un evento `ADDED`, sea cual sea el `K8s Event` que
|
|
101
|
+
selecciones. Ese selector se guarda con el estado del Playground, pero no cambia la simulación.
|
|
102
|
+
|
|
103
|
+
## Diferencias de motor con un trigger real
|
|
104
|
+
|
|
105
|
+
| | Trigger real | Playground |
|
|
106
|
+
|---|---|---|
|
|
107
|
+
| Salida | Estructurada (findings, PSS, informe) | **Texto libre** |
|
|
108
|
+
| `autoTools` | Entrega el catálogo entero al modelo | Hace una **llamada previa** al modelo para que elija las tools, y sólo le pasa esas |
|
|
109
|
+
| Si el modelo no responde texto | Es un error | Hace una **segunda llamada** resumiendo los resultados de las tools |
|
|
110
|
+
|
|
111
|
+
La primera diferencia es la que importa: en el Playground **nunca vas a ver un finding**. Sirve para validar
|
|
112
|
+
que el prompt lleva al modelo por el camino correcto y que las tools devuelven lo que esperas; el formato
|
|
113
|
+
estructurado sólo lo verás cuando lo exportes a un trigger y lo dispares de verdad.
|
|
114
|
+
|
|
115
|
+
La segunda explica por qué `autoTools` sale más caro en el Playground que en producción: hay una llamada
|
|
116
|
+
extra, cuya elección de tools se te muestra como `[Auto tools] selected: ...`.
|
|
117
|
+
|
|
118
|
+
## Llevarse el resultado
|
|
119
|
+
|
|
120
|
+
Los cuatro botones de abajo a la izquierda:
|
|
121
|
+
|
|
122
|
+
| Botón | Qué hace |
|
|
123
|
+
|--------------|------------------------------------------------------------------------------------|
|
|
124
|
+
| **Import** | Carga en el Playground la configuración de **un trigger existente**, eligiendo trigger y versión. Sobrescribe lo que tengas. |
|
|
125
|
+
| **Export** | Convierte el Playground en un trigger: **New trigger** (nuevo, con la versión `v1` activada) o **Add version** (añade una versión desactivada a un trigger existente). Cierra el diálogo. |
|
|
126
|
+
| **Upload** | Carga la configuración desde un fichero JSON local. |
|
|
127
|
+
| **Download** | Guarda la configuración actual en `pinocchio-playground-AAAA-MM-DD.json`. |
|
|
128
|
+
|
|
129
|
+
Y a la derecha, **Save** (persiste el estado y los historiales) y **Cancel** (cierra sin guardar).
|
|
130
|
+
|
|
131
|
+
> ⚠️ **Export → New trigger no se lleva el Kind ni el K8s Event.** El trigger nuevo se crea con el tipo
|
|
132
|
+
> (`artifact` o `business`) y la versión, pero sin `kind` y sin `k8sEvent`, así que un trigger `artifact`
|
|
133
|
+
> recién exportado **no casará con ningún evento** hasta que lo abras en **Config → Trigger** y le pongas el
|
|
134
|
+
> kind a mano. Está recogido en [Límites conocidos](../admin/06-limits.md).
|