Protocolo del Teatro (obras estáticas con zip certificado)
Este repo es el sitio del Teatro (
C:\S_LAB\o-sdk,https://github.com/alephscriptorium-eng/O_SDK.git). La sala vive enhttps://pub.escrivivir.co/teatro/como Sala 03 del Scriptorium.
Estado · WP-O99 (2026-09-18). El Teatro es ya autocontenido en o-sdk: el generador está en git (
pub/rrss-sidecar/), el lore del usuario vive enARCHIVO/LORE/(ignorado por git) y la obra se genera envolumes-dev/teatro/<obra>/. Obra Nº 1 «Aleph Cero»: generación 2026-09-18, 1940 posts. Cómo se hace una obra desde un export de X:RRSS-SIDECAR-PROTOCOL.md. Cómo se cura su puerta semántica («El sistema», «El cantar»):TEATRO-CURADURIA-PROTOCOL.md.
Checklist operativo para publicar una obra en el Teatro: invariantes, alta en el catálogo, deploy, certificación, verificación y realidad del VPS.
Modelo mental. El Teatro separa catálogo de contenido. La portada (
/teatro/) es una plantilla versionada enpub/site-templates/teatro/. Las obras son árboles pesados que NO viajan en git: se generan desde un lore con un sidecar, viven en el volumen de datos del VPS (/srv/oasis/teatro/<obra>/) ypub-web(Caddy) los monta read-only en/srv/site/teatrosobre el mountpoint vacíopub/site/teatro/(el visor).ARCHIVO/LORE/<fuente>/<obra>/ → pub/rrss-sidecar/<fuente>/ → volumes-dev/teatro/<obra>/ → /srv/oasis/teatro/<obra>/ lore (privado) sidecar (git) visor local (:8088) visor en producción
0. Invariantes de una obra
Toda obra del Teatro cumple (la definición ejecutable es pub/rrss-sidecar/twitter_x/lib/guards.py; la corre el build, npm run teatro:check y el pre-vuelo del deploy):
- Cero JavaScript en las páginas generadas: HTML + un CSS propio. El vídeo se sirve con
<video controls preload="metadata">. Excepción declarada:navegador.html, el visor oficial del export de X, limpiado (whitelist de datos, manifest podado, sin CDNs). Es la única página con<script>y los únicos.jsestán enassets/js/y en la whitelist dedata/. Se desactiva conpublish.viewer: false. - Cero recursos externos cargados. Ni CDNs, ni fuentes remotas, ni imágenes de terceros en caliente: la foto de un tuit ajeno se descarga a
data/external_media/o no se muestra. Los enlaces<a href>externos sí están permitidos. - Texto verbatim, citado por id. Nada se resume en lugar de la fuente; el texto ajeno no recuperado no se completa ni se inventa (
AGENTS.mdde la obra). - Nada sensible. Fuera IPs, teléfonos, device tokens, DMs, bloqueos/mutes, likes/follows, chats de IA del export y posts borrados por el autor (se conservan solo en el lore). La obra publica: media propia de los posts visibles, HTML generado, corpus Markdown, stores públicos y los zips.
- Descarga certificada con letrero. SHA-256 visible en la página y firma ed25519 (§4). Las páginas que muestran el hash usan el placeholder literal
__ZIP_SHA256__; el deploy lo estampa.
1. Generar el árbol de la obra
npm run teatro:build -- --obra <obra> # → volumes-dev/teatro/<obra>/ (+ volumes-dev/teatro/index.html)
npm run teatro:check -- --obra <obra> # invariantes
npm run pub:local:up # vista previa: http://localhost:8088/teatro/<obra>/El árbol lo produce un sidecar (RRSS-SIDECAR-PROTOCOL.md). El contrato con el Teatro es solo el árbol: index.html con __ZIP_SHA256__, páginas, assets/, data/ (whitelist), corpus/, indexes/, tools/, AGENTS.md. El pub local ya monta volumes-dev/teatro en /teatro/ (OASIS_PUB_TEATRO_DIR, default ../volumes-dev/teatro).
2. Alta en el catálogo
- Añadir o actualizar la tarjeta de la obra en
pub/site-templates/teatro/index.html(título, copy, cifras,door-linka/teatro/<obra>/). El zip completo es la descarga destacada; el zip ligero y el manifiesto se rotulan «para inspección». BloqueSHA-256 DEL ZIP: __ZIP_SHA256__con enlaces a.sha256y.sig. - Commit en
main. El build y el deploy copian esta portada al árbol antes de subir.
3. Deploy (subida + zips + checksums + firma + verificación)
TEATRO_OBRA=<obra> TEATRO_DRY_RUN=1 npm run devops:teatro:deploy # qué subiría (y qué borraría)
TEATRO_OBRA=<obra> npm run devops:teatro:deploy # en Windows corre dentro de WSL (rsync, python3)Qué hace devops/scripts/deploy-teatro.sh, en orden:
- Pre-vuelo local:
sidecar.py check(invariantes) ysidecar.py manifest→MANIFEST.sha256(hash de cada fichero servido; excluye la portada, que se estampa después, y los artefactos). - Copia la portada del Teatro del repo al árbol.
rsync -a --partial --chmod=D755,F644de solo esa obra y deindex.html→/srv/oasis/teatro/. Reanudable.TEATRO_DELETE=1hace espejo dentro de<obra>/, nunca fuera (así desaparecen del servidor las páginas de posts borrados).- Integridad:
sha256sum -c MANIFEST.sha256en el VPS, antes de estampar nada. - Dos zips en el VPS (evita subir el peso dos veces):
<obra>.zip= todo el árbol con la media, sin comprimir (zip -0), la descarga principal; y<obra>-cerebro.zip= corpus, índices, herramientas y stores, para inspección o gestión. sha256sumde ambos yseddel hash del zip completo en los dos letreros.- Firma en local de
<obra>.zip.sha256,<obra>-cerebro.zip.sha256yMANIFEST.sha256(ssh-keygen -Y sign -n filecon la clave de acceso al VPS; la privada nunca viaja) y sube las.sig+allowed_signers. - Verificación automática (§5). Un fallo termina con código ≠ 0.
Variables: TEATRO_OBRA (obligatoria), LOCAL_TEATRO_DIR, REMOTE_TEATRO_DIR, TEATRO_DRY_RUN, TEATRO_DELETE, TEATRO_SKIP_ZIP, TEATRO_SKIP_VERIFY.
4. Qué se certifica (y cómo lo comprueba un visitante)
- Integridad:
<obra>.zip.sha256(visible además en el letrero),<obra>-cerebro.zip.sha256yMANIFEST.sha256(un hash por fichero servido: permite verificar un mirror sin bajar el zip). - Procedencia: las tres
.sig, firma ssh-ed25519 con la misma clave que abre el VPS. La pública está enallowed_signers(principalteatro@escrivivir.co, namespacefile).
sha256sum -c <obra>.zip.sha256
ssh-keygen -Y verify -f allowed_signers -I teatro@escrivivir.co -n file -s <obra>.zip.sha256.sig < <obra>.zip.sha256
# mirror + verificación fichero a fichero:
wget -r -np -nH --cut-dirs=2 https://pub.escrivivir.co/teatro/<obra>/ && sha256sum -c MANIFEST.sha2565. Verificación post-deploy
La ejecuta el propio deploy. Manualmente: TEATRO_OBRA=<obra> npm run devops:teatro:verify (árbol remoto contra el manifiesto local, recuento, tamaño, permisos). Comprueba: /teatro/, /teatro/<obra>/, /enlaces/, checksums y manifiesto → 200; un .mp4 con Range → 206; data/ip-audit.js y data/direct-messages.js → 404; hash estampado; portada sin <script>; nada world-writable; / y /public/status vivos; y avisa si /teatro/no-existe/ no da 404 (§6).
Respaldo de lo no regenerable: TEATRO_OBRA=<obra> npm run devops:teatro:backup (portada estampada, firmas, manifiesto y los stores de voces y enlaces del lore). El árbol servido no se respalda: se regenera. Los exports son responsabilidad del usuario (ARCHIVO/LORE/README.md).
6. Realidad del VPS — leer antes de tocar
- El stack vivo corre desde
/opt/oasis-scriptorium/OASIS_PUB(layout antiguo, sin git), no desde el checkoutpub/que describedevops/MIGRATION-2026-07.md. La migración sigue pendiente; los ficheros se suben in place (cat >), nunca reemplazando el inode de un bind de fichero. - El mount del Teatro se añadió al compose vivo a mano (backup
docker-compose.pub.yml.bak-teatro);OASIS_PUB_TEATRO_DIR=/srv/oasis/teatroestá en.envy.env.prodde ese directorio. - La obra vive en el volumen de datos (
/dev/xvdb,/srv/oasis/teatro), no en el disco de sistema.verify-debian13-base.shlo comprueba (existe, permisos, nada world-writable) yhub-disk.sh statuslo mide. - Bloque
@teatrodel Caddyfile (pub/caddy/Caddyfile): 404 real bajo/teatro/(sin el fallback a la landing delhandlegenérico),Cache-Control, CSP estricta para las páginas generadas y CSP propia paranavegador.html. El Caddyfile vivo es compartido por más vhosts: backup → subir →docker exec oasis-pub-web caddy validate --config /etc/caddy/Caddyfile→caddy reload. Nuncarestart pub-web. pub/site/scriptorium/index.htmldel repo y el vivo divergen a propósito (token público de PUBLIC_ROOM). Al desplegar el vestíbulo hay que fusionar, no pisar.deploy-site.shusarsync --deletesin exclusiones: no apuntarlo aOASIS_PUB/sitesin revisar los*.bak-*.- Recrear solo el edge:
cd /opt/oasis-scriptorium/OASIS_PUB && docker compose --env-file .env.prod -f docker-compose.pub.yml up -d pub-web. El project name coincide con el del repo: el día de la migración Docker adopta contenedores y red.