diff --git a/workloads/docs-portal/docs/devsecops/index.md b/workloads/docs-portal/docs/devsecops/index.md
new file mode 100644
index 0000000..23d5761
--- /dev/null
+++ b/workloads/docs-portal/docs/devsecops/index.md
@@ -0,0 +1,91 @@
+# DevSecOps
+
+Fase 2 del lab: integrar tooling de seguridad al pipeline de Gitea
+Actions, empezando por un solo repo de referencia — el frontend de ARI
+Shopping (`workloads/ecommerce`, `.gitea/workflows/build.yaml`) — antes
+de replicarlo a `commerce-backend` y `docs-portal`.
+
+Cinco herramientas, cada una respondiendo una pregunta distinta:
+
+| Herramienta | Pregunta que responde | Modo |
+|---|---|---|
+| [Gitleaks](gitleaks.md) | ¿Hay un secreto commiteado? | **Bloquea** |
+| [Trivy — imagen](trivy.md) | ¿La imagen tiene una CVE conocida? | CRITICAL **bloquea**, HIGH informa |
+| [Trivy — IaC](trivy.md) | ¿El manifiesto de Kubernetes es inseguro? | Informa |
+| [Semgrep (SAST)](sast.md) | ¿El código tiene un patrón inseguro conocido? | Informa (auditoría) |
+| [Syft (SBOM)](sbom.md) | ¿Qué paquetes exactos quedaron en la imagen? | Informa (inventario) |
+| [Cosign](cosign.md) | ¿Esta imagen es exactamente la que armó el pipeline? | **Bloquea** (smoke test) |
+
+## El pipeline completo
+
+```mermaid
+flowchart TD
+ subgraph J1["job: gitleaks (push + pull_request)"]
+ A1[checkout] --> A2["gitleaks detect --no-git"]
+ end
+
+ A2 -- "secreto encontrado" --> XA["❌ pipeline detenido
build nunca arranca"]
+ A2 -- "limpio" --> B0
+
+ subgraph J2["job: build (solo push a main, needs: gitleaks)"]
+ B0[checkout + validaciones de código] --> B1["Trivy IaC
(frontend.yaml)"]
+ B1 -.informa.-> B2["Semgrep SAST
(audit mode)"]
+ B2 -.informa.-> B3[login registry]
+ B3 --> B4["docker build
(push: false, load: true)"]
+ B4 --> B5["Trivy imagen
CRITICAL"]
+ B5 -- "CRITICAL con fix" --> XB["❌ detenido
no se sube la imagen"]
+ B5 -- "sin CRITICAL" --> B6["Trivy imagen
HIGH (informa)"]
+ B6 --> B7["Syft → SBOM
(CycloneDX + SPDX)"]
+ B7 --> B8["docker push"]
+ B8 --> B9["cosign sign"]
+ B9 --> B10["cosign verify
(smoke test)"]
+ B10 -- "firma inválida" --> XC["❌ detenido"]
+ B10 -- "firma válida" --> B11["actualizar frontend.yaml
(GitOps, Argo CD sincroniza)"]
+ end
+```
+
+## Por qué este orden
+
+- **Gitleaks corre en un job aparte, antes que todo lo demás** — incluso
+ antes que el checkout completo del job de build. Si hay un secreto,
+ no tiene sentido gastar minutos de build en un commit que hay que
+ rechazar igual. Es el único check que corre también en `pull_request`,
+ no solo en push a `main`.
+- **Trivy IaC y Semgrep corren antes del build de Docker.** Ninguno de
+ los dos necesita la imagen construida — analizan manifiestos y código
+ fuente respectivamente — así que si algo llamativo apareciera ahí, se
+ sabe temprano, sin esperar el build (que es el paso más lento del
+ pipeline).
+- **Trivy imagen corre después del build pero antes del push.** Por eso
+ el build usa `push: false, load: true`: la imagen queda en el daemon
+ local del runner, escaneable, pero no sale hacia el registry hasta
+ pasar el gate de CRITICAL.
+- **Syft (SBOM) corre sobre la imagen ya validada**, antes del push —
+ documenta exactamente lo que se está por publicar.
+- **Cosign firma después del push exitoso** (no tiene sentido firmar
+ algo que no llegó al registry) y se verifica en el mismo pipeline como
+ smoke test.
+
+## Qué pasa si cada uno falla
+
+| Si falla... | El pipeline... |
+|---|---|
+| Gitleaks | Se detiene ahí mismo. El job `build` nunca arranca (`needs: gitleaks`). Nada se construye. |
+| Trivy IaC | No se detiene — el hallazgo queda en el log, informativo. |
+| Semgrep | No se detiene — mismo criterio: primera vuelta en modo auditoría. |
+| Trivy imagen (CRITICAL) | Se detiene después del build, antes del push. La imagen con la CVE nunca llega al registry. |
+| Trivy imagen (HIGH) | No se detiene — se reporta para revisar en conjunto. |
+| Syft | Si el propio comando falla (no si "encuentra algo" — un SBOM no tiene hallazgos que bloqueen), el pipeline se detiene por error real de la herramienta. |
+| Cosign sign/verify | Se detiene. Si la imagen se firmó pero no verifica, algo está mal con las llaves o con la imagen — no se continúa con la promoción GitOps. |
+
+## Qué falta después de esta primera vuelta
+
+- Revisar en conjunto los hallazgos de Trivy IaC y Semgrep (hoy
+ informativos) y decidir cuáles pasan a bloquear.
+- Decidir si Trivy imagen sube el umbral de bloqueo a HIGH una vez que
+ el backlog de CVEs conocidas esté bajo control.
+- Admission controller en el cluster que verifique la firma de Cosign
+ antes de dejar correr un Pod (ver [cosign.md](cosign.md)) — hoy la
+ verificación es solo un smoke test dentro del propio pipeline.
+- Replicar este mismo patrón a `commerce-backend` (Medusa) y
+ `docs-portal`, adaptando lo que corresponda a cada stack.
diff --git a/workloads/docs-portal/mkdocs.yml b/workloads/docs-portal/mkdocs.yml
index 5dd51ed..dbcc013 100644
--- a/workloads/docs-portal/mkdocs.yml
+++ b/workloads/docs-portal/mkdocs.yml
@@ -99,6 +99,7 @@ nav:
- Inicio: guia-estudiante/README.md
- Conceptos básicos: guia-estudiante/conceptos-basicos.md
- DevSecOps:
+ - Resumen: devsecops/index.md
- Gitleaks (secretos): devsecops/gitleaks.md
- Trivy (imagen + IaC): devsecops/trivy.md
- SAST (Semgrep): devsecops/sast.md