PROCEDIMIENTO — Deploy Blog y Wiki a Cloudflare Pages
Para agentes IA y operador humano. Esta nota describe de forma exhaustiva cómo desplegar los dos sitios web de Sammi De Blas (blog sammideblas.com y wiki doc.sammideblas.com) a Cloudflare Pages desde GitHub vía GitHub Actions. Un
git pushamaindespliega solo. Tiempo medio de deploy: ~60 segundos.Origen: documentación canónica tras la unificación de deploy realizada el 2026-07-06. Cualquier divergencia entre esta nota y el estado real ⊂ avisar y reescribir.
0. TL;DR (resumen para agent que solo quiere ejecutar)
| Blog | Wiki | |
|---|---|---|
| Dominio público | sammideblas.com (+ blog.sammideblas.com, www.sammideblas.com) | doc.sammideblas.com |
| Repo GitHub | PoisonXploIT/blog (privado) | PoisonXploIT/wiki (privado) |
| CF Pages project | blog | sammideblas-wiki |
| CF Pages project ID | bdba5fc0-9e9a-4d5f-b9bb-2d92ec754551 | 18d6cbcc-89a6-44ba-985e-7a9fa128707d |
| pages.dev subdomain | blog-f5e.pages.dev | sammideblas-wiki.pages.dev |
| Ruta local principal | C:\Users\Sammi De Blas\hermes_data\blog-build | C:\Users\Sammi De Blas\hermes_data\wiki-build |
| Workflow | .github/workflows/deploy.yml | .github/workflows/deploy.yml |
| Deploy manual (en máquina) | git push origin main | git push origin main |
Cuenta Cloudflare: ID 43c5d4a754f2951ba36185df5deef60c.
Secrets en cada repo GitHub (configurados una sola vez, ver sección 5):
CLOUDFLARE_API_TOKEN(valor del API Token CF, scopeAccount → Cloudflare Pages → Edit)CLOUDFLARE_ACCOUNT_ID=43c5d4a754f2951ba36185df5deef60c
0.5. Dónde está todo (mapa de localización — para agentes sin contexto)
Antes de hacer nada, saber DÓNDE vive cada cosa es más importante que saber CÓMO se hace. Esta sección es el mapa. Si necesitas
X, mira la fila deXy ve a esa ubicación. Si la ubicación no existe en tu máquina, clona/descarga según se indique.
| ¿Qué necesitas? | Dónde está | Si no está en tu máquina |
|---|---|---|
| Fuente del blog (Quartz, posts) | Repo GitHub privado PoisonXploIT/blog | gh repo clone PoisonXploIT/blog blog-build |
| Fuente del wiki (Quartz, vault público) | Repo GitHub privado PoisonXploIT/wiki | gh repo clone PoisonXploIT/wiki wiki-build |
| Ruta local por defecto del blog (máquina de referencia) | C:\Users\Sammi De Blas\hermes_data\blog-build\ | clonar arriba a esa ruta |
| Ruta local por defecto del wiki | C:\Users\Sammi De Blas\hermes_data\wiki-build\ | idem |
| Vault Obsidian completo (editorial, noteswise) | C:\Users\Sammi De Blas\Desktop\OBSIDIAN VAULT\Destino\ | autosincronizado por Syncthing (no en git) |
| Esta nota canónica (procedimiento deploy super-detallado, 42 KB) | 3 copias espejo: (1) Destino\PROCEDIMIENTOS\PROCEDIMIENTO - Deploy Blog y Wiki a Cloudflare Pages.md en el vault Obsidian · (2) .github\DEPLOY.md del repo PoisonXploIT/blog · (3) content\PROCEDIMIENTOS\deploy-blog-wiki.md del repo PoisonXploIT/wiki | La (2) y (3) llegan con git clone; la (1) llega vía Syncthing del vault |
| Índice general / context pack para agentes | Destino\PROCEDIMIENTOS\PROCEDIMIENTO - INDICE GENERAL Agentes IA.md en el vault del Escritorio | Syncthing |
AGENTS.md (puntero corto para agentes que clone un repo) | Raíz de cada repo: PoisonXploIT/blog/AGENTS.md y PoisonXploIT/wiki/AGENTS.md | viene con el git clone |
| Workflow de deploy (CI/CD) | .github\workflows\deploy.yml en cada repo | viene con el git clone |
Secrets de Cloudflare (CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID) | GitHub Secrets de PoisonXploIT/blog y PoisonXploIT/wiki (no en texto plano en ningún lado) | gh secret set CLOUDFLARE_API_TOKEN --repo PoisonXploIT/blog --body "<token>" y análogo en /wiki |
| Cloudflare Account ID | 43c5d4a754f2951ba36185df5deef60c (no es secreto, está en dashboard CF) | — |
| CF Pages project IDs | blog = bdba5fc0-9e9a-4d5f-b9bb-2d92ec754551 · sammideblas-wiki = 18d6cbcc-89a6-44ba-985e-7a9fa128707d | — |
| Dominios en producción | Blog: sammideblas.com, blog.sammideblas.com, www.sammideblas.com · Wiki: doc.sammideblas.com | — |
| Token Cloudflare real (el valor, no el nombre del secret) | Dashboard Cloudflare → My Profile → API Tokens → token blog-wiki-deploy | crear uno nuevo (sección 5.1) → fijarlo en los 4 secrets (2 repos × 2 secrets) |
| Logs históricos de wrangler (del deploy manual viejo pre-Actions) | hermes_data\home\.config\.wrangler\logs\ (referencia, no tocar) | no son necesarios para operar |
HOME de Hermes / agente (sensible: auth.json, .ssh, .env, config.yaml, state.db, sessions/, kanban.db…) | C:\Users\Sammi De Blas\hermes_data\ (raíz) | nunca se commitea a GitHub; no se migra salvo reinstalación explícita de Hermes |
| Repos de código propios (Ciber_Radar, sec-dashboard, Gravity-SOC, Auditing_with_PowerShell, etc.) | Cada uno en su propio remote PoisonXploIT/<name> | gh repo clone PoisonXploIT/<name> |
| Subdominio sec.sammideblas.com | PoisonXploIT/sec-dashboard (no en CF Pages; pendiente confirmar hosting) | — |
Punto de arranque mínimo para un agente en máquina limpia
# 0) Instalar: git, Node 22+, npm 10.9+, gh CLI
gh auth login # autenticar en navegador
gh auth refresh -h github.com -s workflow # solo si vas a editar .github/workflows/*
# 1) Clonar los dos repos que importan para el deploy:
gh repo clone PoisonXploIT/blog <ruta>/blog-build
gh repo clone PoisonXploIT/wiki <ruta>/wiki-build
# 2) Abrir y leer (en este orden):
# <ruta>/blog-build/AGENTS.md ← puntero corto + reglas
# <ruta>/blog-build/.github/DEPLOY.md ← procedimiento canónico completo
# (o equivalentes en wiki-build: AGENTS.md y content/PROCEDIMIENTOS/deploy-blog-wiki.md)
# 3) Para publicar un post:
# - crea content/posts/0x71-siguiente.md (plantilla en DEPLOY.md sec 4.1)
# - git add; git commit -m "post: 0x71 ..."; git push origin main
# - gh run list --repo PoisonXploIT/blog --limit 1 (verificar success)A partir de ese punto no necesitas contexto adicional. Todo el procedimiento (frontmatter, plantilla, crear token CF si caduca, permisos OAuth, troubleshooting) está en el procedimiento canónico que acabas de clonar.
1. Arquitectura general
┌─────────────────────────────────────────────────────────────────┐
│ Máquina local (Windows / Linux) │
│ ┌───────────────────────────┐ ┌───────────────────────────┐ │
│ │ hermes_data\blog-build │ │ hermes_data\wiki-build │ │
│ │ (Quartz, fuente blog) │ │ (Quartz, fuente wiki) │ │
│ └─────────────┬─────────────┘ └─────────────┬─────────────┘ │
└────────────────┼────────────────────────────────┼────────────────┘
│ git push origin main │ git push origin main
▼ ▼
┌────────────────────┐ ┌────────────────────┐
│ GitHub: PoisonXploIT/blog │ GitHub: PoisonXploIT/wiki │
│ .github/workflows/deploy.yml │ .github/workflows/deploy.yml │
└─────────┬──────────┘ └─────────┬──────────┘
│ triggers on push to main │ triggers on push to main
▼ ▼
┌────────────────────┐ ┌────────────────────┐
│ GitHub Actions runner │ GitHub Actions runner │
│ - actions/checkout@v4 │ - actions/checkout@v4 │
│ - actions/setup-node@v4 (22) │ - actions/setup-node@v4 (22) │
│ - npm ci │ - npm ci │
│ - npx quartz build │ - npx quartz build │
│ - cloudflare/wrangler-action@v3│ - cloudflare/wrangler-action@v3 │
└─────────┬──────────┘ └─────────┬──────────┘
│ wrangler pages deploy │ wrangler pages deploy
│ public --project-name=blog │ public --project-name=sammideblas-wiki
▼ ▼
┌────────────────────┐ ┌────────────────────┐
│ CF Pages project 'blog' │ CF Pages project 'sammideblas-wiki' │
│ - blog-f5e.pages.dev │ - sammideblas-wiki.pages.dev │
│ - alias: sammideblas.com │ - alias: doc.sammideblas.com │
│ - alias: blog.sammideblas.com │ │
│ - alias: www.sammideblas.com │ │
└────────────────────┘ └────────────────────┘
Componentes
| # | Componente | Rol |
|---|---|---|
| 1 | Repos GitHub privados | Fuente de verdad + control de versiones. Privados porque contienen drafts y documentos internos. |
| 2 | Quartz 4.5.2 | Generador de sitio estático. Convierte Markdown en HTML en public/. Configuración en quartz.config.ts y quartz.layout.ts. |
| 3 | GitHub Actions deploy.yml | Workflow en .github/workflows/deploy.yml. Dispara build + wrangler pages deploy en cada push a main. |
| 4 | Secrets de GitHub | Variables cifradas en el repo: CLOUDFLARE_API_TOKEN y CLOUDFLARE_ACCOUNT_ID. NO se commitean nunca. |
| 5 | Cloudflare Pages | Hosting del sitio estático. Cada deployment queda versionado y asociado a un commit hash de GitHub. |
| 6 | wrangler-action@v3 | Action oficial de Cloudflare que ejecuta wrangler pages deploy public --project-name=<name> dentro del runner. |
Por qué esta arquitectura (motivación)
Antes del 2026-07-06:
- El blog no tenía repo GitHub (
PoisonXploIT/blogdevolvía 404). - El deploy era manual con
npx wrangler pages deploydesde un host Linux en/opt/data/blog-build. - El workflow del wiki apuntaba a GitHub Pages (no estaba activado → roto).
- Una segunda máquina nunca encontraba la fuente ni el flujo de deploy.
Después:
- Cualquier máquina solo hace
git cloney congit pushdespliega solo. - Cualquier agente IA encuentra el procedimiento leyendo
AGENTS.md+.github/DEPLOY.md(blog) ocontent/PROCEDIMIENTOS/deploy-blog-wiki.md(wiki). - No hace falta token de Cloudflare local (los secrets viven en GitHub).
2. Prerequisites (lo que necesitas instalado en cada máquina)
Software obligatorio
- Git >= 2.40 (https://git-scm.com)
- Node.js >= 22 (verificado con 22.18.0; los workflows exigen 22)
- npm >= 10.9.2 (viene con Node)
- GitHub CLI
ghautenticada con scoperepo+workflow(ver sección 7)
Opcional (para deploys locales sin Actions)
- wrangler (
npm install -g wrangleronpx wrangler@latest) — solo si quieres hacer deploys locales manuales. - Un API Token de Cloudflare con scope
Account → Cloudflare Pages → Edit(ver sección 5).
Cuentas necesarias
- Cuenta de GitHub con acceso a los repos
PoisonXploIT/blogyPoisonXploIT/wiki. - Cuenta de Cloudflare con el proyecto Pages
blogysammideblas-wiki(cada ID en sección 0). Ten solo una cuenta (ID43c5d4a754f2951ba36185df5deef60c).
Verificar instalacion
git --version
node --version # debe ser >= 22
npm --version # >= 10.9.2
gh --version
gh auth status # debe mostrar "Logged in to github.com account PoisonXploIT"3. Setup en una máquina nueva (one-time)
# 1. Elegir ubicación (ej. hermes_data\)
mkdir C:\Users\Sammi De Blas\hermes_data
cd C:\Users\Sammi De Blas\hermes_data
# 2. Clonar ambos repos (privados; gh pedira auth la primera vez)
gh repo clone PoisonXploIT/blog blog-build
gh repo clone PoisonXploIT/wiki wiki-build
# 3. Instalar dependencias locales (solo para build local / tests)
cd blog-build ; npm install ; cd ..\wiki-build ; npm install ; cd ..
# 4. Verificar estado (debería estar todo verde)
cd blog-build ; git status ; cd ..\wiki-build ; git statusA partir de aquí:
- Para desplegar tras un cambio: hacer commit +
git push origin main. No hace falta wrangler ni token CF local — el deploy lo ejecuta GitHub Actions con los secrets del repo. - Para probar localmente antes de pushear:
npx quartz build --serve(levanta un preview enhttp://localhost:8080).
4. Flujo de trabajo diario
4.1 Publicar un post nuevo en el blog
cd C:\Users\Sammi De Blas\hermes_data\blog-buildPaso 1 — crear el archivo Markdown. Slug sugerido: 0xNN-titulo-con-guiones.md dentro de content\posts\. Numeración hexadecimal consecutiva: el último publicado es 0x69 (CySA Domain 3), el más reciente en master es 0x70 (17 days listening to the air). El siguiente debería ser 0x71.
Plantilla frontmatter obligatoria:
---
title: "0xNN: Título del Post"
date: 2026-07-06 # ISO date (YYYY-MM-DD). Opcional: añadir T HH:mm:ss+00:00
draft: false # false = se publica. true = NO se publica
description: "Una linea de descripción para SEO y preview"
tags:
- tag1
- tag2
- tag3
---
# Título del post
Contenido en Markdown / HTML embebido...El script
new-post.sh(bash) automatiza la creación del archivo y lo inserta encontent/index.mdsi tienes ”## Posts” como cabecera. Ej:./new-post.sh "0x70: 17 days listening to the air".
Paso 2 — preview local (opcional pero recomendado):
npx quartz build --serve
# abrir http://localhost:8080
# Ctrl+C para pararPaso 3 — commit y push:
git add content\posts\0xNN-slug.md
git commit -m "post: 0xNN Título del post"
git push origin mainPaso 4 — verificar el deploy:
gh run list --repo PoisonXploIT/blog --limit 1
# esperar a que el ultimo run ponga "completed / success"
# (suele tardar ~60 segundos)El post aparece en https://sammideblas.com/posts/0xNN-slug. El sitemap y el RSS (https://sammideblas.com/index.xml) se regeneran automáticamente.
4.2 Publicar un cambio en la wiki
cd C:\Users\Sammi De Blas\hermes_data\wiki-build
# editar el archivo o crear una nota nueva en content\<carpeta>\<nota>.md
git add content\<ruta-modificada>
git commit -m "wiki: descripción del cambio"
git push origin main
gh run list --repo PoisonXploIT/wiki --limit 1
# producción: https://doc.sammideblas.comCaracterística importante del wiki: ignorePatterns
El wiki publica solo una parte del vault. En quartz.config.ts hay un ignorePatterns que excluye carpetas/nombres sensibles del build público:
ignorePatterns: [
"LOGS RAW", "CORPORATIVO", "Templates", "Anexos", ".obsidian",
".smart-env", "BMO", "Chats", "Clippings", "Agentes IA", "COPILOT",
"NOTAS DIARIAS SUELTAS", "PROYECTO MVK2", "Troubleshooting", "TRENDING", "BLOG",
"000_DASHBOARD_PRINCIPAL.md", "00_CENTRAL_COMMANDS.md",
"PROCEDIMIENTO*", "PROCEDIMIENTOS*", "*Index*", "brave device log*"
]
→ Cualquier nota en content/PROCEDIMIENTOS/ se commitea al repo (llega a todas las máquinas) PERO NO se publica en doc.sammideblas.com. That’s by design: es donde viven notas internas como IDs de CF Pages, comandos con tokens, runbooks operativos.
Si creas una carpeta nueva sensible, añádela también a
ignorePatternsantes de commitear.
.gitignore del wiki
Estas rutas nunca se commitean:
node_modules/ public/ .obsidian/
.smart-env/ .DS_Store quartz/.cache/*.bak
content/LOGS RAW/ content/TRENDING/ content/Agentes IA/
content/COMP TIA CySA+/ content/NOTAS DIARIAS SUELTAS/ content/PROYECTO MVK2/
content/BMO/ content/COPILOT/ content/Chats/
content/Clippings/ content/CORPORATIVO/ content/Troubleshooting/
content/BLOG/ content/000_DASHBOARD_PRINCIPAL.md
content/00_CENTRAL_COMANDOS.md
NOTA: el 2026-07-06 se quitó
content/PROCEDIMIENTO*del.gitignoreporque queremos que esa carpeta sí se versionee (para que las notas internas lleguen a todas las máquinas). La exclusión del build público la haceignorePatternsde Quartz, no.gitignore. No reactivar la regla en.gitignoreo se rompe la sincronización.
5. Cómo crear el token de Cloudflare (one-time + cuando expire)
El token NO se commitea nunca. Se guarda en GitHub Secrets con
gh secret sety se destruye la copia local.
5.1 Crear el token en el dashboard
- Ir a https://dash.cloudflare.com/profile/api-tokens
- Botón “Create Token”.
- Bajar hasta el final de la lista de plantillas → junto a “Custom Token” → “Get started”.
- La plantilla “Edit Cloudflare Workers” no vale — la de Pages no existe como plantilla predefinida; por eso se usa Custom Token.
- Token name:
blog-wiki-deploy(o algo descriptivo; sirve para identificarlo luego en la lista). - Permissions:
- Primer menú: Account
- Segundo menú: Cloudflare Pages
- Tercer menú: Edit
- Account Resources:
Include→Specific account→ la cuenta con ID43c5d4a754f2951ba36185df5deef60c(oAll accountssi solo hay una). - (Opcional pero recomendado) Client IP Address Filtering: añadir la IP de salida habitual para reducir exposición.
- (Opcional) TTL: establecer fecha de expiración o dejar sin caducidad.
- Continue to summary → Create Token.
- Copiar el valor generado (formato
cfut_...o similar). Solo aparece una vez. Guardarlo en un gestor de contraseñas.
5.2 Subir el token a los repos GitHub como secret
$env:CLOUDFLARE_API_TOKEN = "<token-pegado-aqui>"
gh secret set CLOUDFLARE_API_TOKEN --repo PoisonXploIT/blog --body $env:CLOUDFLARE_API_TOKEN
gh secret set CLOUDFLARE_ACCOUNT_ID --repo PoisonXploIT/blog --body "43c5d4a754f2951ba36185df5deef60c"
gh secret set CLOUDFLARE_API_TOKEN --repo PoisonXploIT/wiki --body $env:CLOUDFLARE_API_TOKEN
gh secret set CLOUDFLARE_ACCOUNT_ID --repo PoisonXploIT/wiki --body "43c5d4a754f2951ba36185df5deef60c"5.3 Verificar que los secrets están
gh secret list --repo PoisonXploIT/blog # debe mostrar CLOUDFLARE_API_TOKEN y CLOUDFLARE_ACCOUNT_ID
gh secret list --repo PoisonXploIT/wiki5.4 Limpiar variables del entorno local
Remove-Item Env:\CLOUDFLARE_API_TOKEN
# (alternativa: cerrar la terminal)5.5 Regenerar un token
Si el token caduca o se compromete (p. ej. quedó en un log de chat): ir a la lista de tokens en el dashboard → sobre el token blog-wiki-deploy → Roll (regenera un nuevo valor) o Delete y crear uno nuevo. Repetir 5.2.
6. Instalación/auth de la GitHub CLI (gh)
6.1 Login inicial
gh auth login
# elegir GitHub.com → HTTPS → autenticar en navegador6.2 Scope workflow (necesario solo si vas a tocar .github/workflows/*.yml)
GitHub exige scope workflow para pushear commits que modifican archivos bajo .github/workflows/. Si un push falla con:
! [remote rejected] main -> main (refusing to allow an OAuth App to create or update workflow ... without 'workflow' scope)
Ejecuta (abre navegador para autorizar):
gh auth refresh -h github.com -s workflowVerificar:
gh auth status
# Token scopes debe listar 'workflow' entre los demasEl 2026-07-06 lo hicimos y los scopes quedaron como:
admin:org admin:org_hook admin:public_key admin:repo_hook delete_repo gist repo workflow write:packages.
6.3 Token clásico como alternativa
Si gh auth refresh no funciona (token OAuth no actualizable), crear un Classic PAT en https://github.com/settings/tokens con scopes repo + workflow, y luego:
$env:GH_TOKEN = "<token-classic>"
# o persistente:
gh auth login --with-token⚠️ El token clásico también queda en logs si lo pegas en chat/terminal sin cuidado. Mejor usar
gh auth refreshinteractivo si se puede.
7. Workflows (referencia yaml completa)
7.1 Blog .github/workflows/deploy.yml
name: Deploy Blog to Cloudflare Pages
on:
push:
branches: [main]
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- name: Install dependencies
run: npm ci
- name: Build Quartz
run: npx quartz build
- name: Deploy to Cloudflare Pages
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy public --project-name=blog7.2 Wiki .github/workflows/deploy.yml
Idéntico salvo el --project-name:
name: Deploy Wiki to Cloudflare Pages
on:
push:
branches: [main]
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- name: Install dependencies
run: npm ci
- name: Build Quartz
run: npx quartz build
- name: Deploy to Cloudflare Pages
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy public --project-name=sammideblas-wiki7.3 Detalles de los pasos
| Step | Qué hace | Por qué |
|---|---|---|
actions/checkout@v4 con fetch-depth: 0 | Clona el repo completo (toda la historia) | Necesario para que Quartz pueda calcular fechas de modified de cada nota |
actions/setup-node@v4 (Node 22) + cache: npm | Instala Node con caché de npm | Cuellos de la versión 22 exigida en package.json (engines.node: ">=22") |
npm ci | Instala dependencias usando package-lock.json (reproducible) | Mejor que npm install en CI |
npx quartz build | Genera public/ con el sitio estático | Mismo comando que en local |
cloudflare/wrangler-action@v3 | Ejecuta wrangler pages deploy public --project-name=... | Sube public/ a CF Pages. Detecta automáticamente branch y commit hash para etiquetar el deployment |
El workflow también dispara en
workflow_dispatch(manual desde la web de Actions) — útil para re-deployar sin tener que hacer un commit vacío.
7.4 Workflows legacy/auxiliares (NO tocar salvo saber qué haces)
En .github/workflows/ de ambos repos existen otros archivos:
ci.yaml— checks de TypeScript + Prettierbuild-preview.yaml+deploy-preview.yaml— previews para Pull Requests (legados de Quartz upstream, no usados activamente porque no hacemos PRs)docker-build-push.yaml— solo en algunosdependabot.yml— actualiza dependencias automáticamente
Los dos workflows deploy.yml son los únicos que importan para el deploy.
8. Verificar un deploy
8.1 Via GitHub Actions
# Últimos runs
gh run list --repo PoisonXploIT/blog --limit 3
gh run list --repo PoisonXploIT/wiki --limit 3
# Detalle de un run concreto (watch en vivo)
gh run watch <run-id> --repo PoisonXploIT/blog --exit-status
# ó
gh run view <run-id> --repo PoisonXploIT/blog --json status,conclusion,jobs8.2 Via API de Cloudflare (necesita token en entorno)
$env:CLOUDFLARE_API_TOKEN = "<token>"
$acc = "43c5d4a754f2951ba36185df5deef60c"
# Último deployment del blog
Invoke-RestMethod `
-Uri "https://api.cloudflare.com/client/v4/accounts/$acc/pages/projects/blog/deployments?per_page=1" `
-Headers @{Authorization="Bearer $env:CLOUDFLARE_API_TOKEN"}
# Lo mismo para el wiki
Invoke-RestMethod `
-Uri "https://api.cloudflare.com/client/v4/accounts/$acc/pages/projects/sammideblas-wiki/deployments?per_page=1" `
-Headers @{Authorization="Bearer $env:CLOUDFLARE_API_TOKEN"}8.3 Endpoints públicos
- Blog: https://sammideblas.com (sitemap en
https://sammideblas.com/sitemap.xml, RSS enhttps://sammideblas.com/index.xml) - Wiki: https://doc.sammideblas.com
Tras un push, el deployment CF Pages tarda ~1 min en estar activo en el dominio custom. La URL
https://<id>.blog-f5e.pages.dev(preview subdomain) está disponible inmediatamente بعد del job, mientras que el dominio custom puede tardar unos segundos más.
8.4 Conectar los dominios custom a un deployment
Ya configurado en el dashboard de Cloudflare Pages para cada proyecto:
blog→sammideblas.com,blog.sammideblas.com,www.sammideblas.com(CNAMEs ablog-f5e.pages.dev).sammideblas-wiki→doc.sammideblas.com(CNAME asammideblas-wiki.pages.dev).- Production branch
mainen ambos. Cada push a main crea un deployment production (no preview).
Si añadieras un dominio nuevo: dashboard de CF → Pages → el proyecto → “Custom domains” → “Set up a custom domain”. Cloudflare te guía automáticamente.
9. Deploy local manual (alternativa a Actions, sin GitHub)
Útil para pruebas o cuando Actions está caído. Necesitas el API token de CF en variables.
$env:CLOUDFLARE_API_TOKEN = "<token>"
$env:CLOUDFLARE_ACCOUNT_ID = "43c5d4a754f2951ba36185df5deef60c"
cd C:\Users\Sammi De Blas\hermes_data\blog-build # o wiki-build
# Build
npx quartz build
# genera public\
# Deploy (el flag --commit-dirty=true evita el warning de working tree sucio)
npx wrangler@latest pages deploy public --project-name=blog --commit-dirty=true
# o
npx wrangler@latest pages deploy public --project-name=sammideblas-wiki --commit-dirty=true⚠️ El deploy local no dispara Actions ni commit. Eso significa que el estado de Cloudflare y GitHub pueden quedar desincronizados. Úsalo solo en emergencias o pruebas; para producción, siempre
git push.
Para hacer el deploy local pero dejar el repo en el mismo commit que el deployment: git add . && git commit -m "manual deploy" && git push, después el siguiente git push de alguien re-deployará con el mismo contenido.
10. Cómo añadir un subdominio nuevo (p. ej. sec.sammideblas.com)
Si en el futuro quieres publicar otro Quartz (u otro sitio) en un subdominio de sammideblas.com:
10.1 Crear proyecto en Cloudflare Pages
- Dashboard CF → Workers & Pages → Create application → Pages → Upload assets (o “Connect to Git” si es repo nuevo).
- Nomear el proyecto (p. ej.
sammideblas-sec). - NO hace falta build configuration si subes el
public/directamente desde CI. - Apuntar el subdominio: proyecto → Custom domains → Set up a custom domain →
sec.sammideblas.com. Cloudflare añade el CNAME automáticamente.
10.2 Crear repo en GitHub
gh repo create PoisonXploIT/sec-source --private
# clonar, montar Quartz, commitear10.3 Fijar secrets + workflow
gh secret set CLOUDFLARE_API_TOKEN --repo PoisonXploIT/sec-source --body $env:CLOUDFLARE_API_TOKEN
gh secret set CLOUDFLARE_ACCOUNT_ID --repo PoisonXploIT/sec-source --body "43c5d4a754f2951ba36185df5deef60c"Copia el deploy.yml del blog cambiando --project-name=sammideblas-sec. Commit + push.
10.4 Documentar
- Actualizar esta nota (secciones 0 y 3 con el nuevo repo/proyecto/ruta local).
- Copiar la actualización a
.github/DEPLOY.mdyAGENTS.mdde los repos hermanos. - Añadir el nuevo subdominio al
ignorePatterns/baseUrldel Quartz correspondiente.
11. Estado de los CF Pages projects (tabla de referencia)
| Blog | Wiki | |
|---|---|---|
| Nombre proyecto | blog | sammideblas-wiki |
| Project ID | bdba5fc0-9e9a-4d5f-b9bb-2d92ec754551 | 18d6cbcc-89a6-44ba-985e-7a9fa128707d |
| pages.dev subdomain | blog-f5e.pages.dev | sammideblas-wiki.pages.dev |
| Alias (custom domains) | sammideblas.com, blog.sammideblas.com, www.sammideblas.com | doc.sammideblas.com |
| Production branch | main | main |
| Fecha creación project | 2026-05-09 | 2026-05-14 |
| Framework framework | "" (Quartz detecta HTML estático) | "" |
compatibility_date | 2026-05-09 | 2026-05-14 |
| Last deployment type | ad_hoc (originally) / wrangler-action (now) | ad_hoc originally / wrangler-action now |
| Build command (CF config) | null (build hecho en GH Actions) | null |
| Build image major version | 3 | 3 |
⚠️ Algunos de estos campos (Build command, compatibility_date, build_image_major_version) son internos de Cloudflare y no se tocan nunca desde aquí. Solo aparecen como referencia.
12. Documentación para agentes (dónde vive cada copia)
| Ubicación | Visible para | Rol |
|---|---|---|
C:\Users\Sammi De Blas\Desktop\OBSIDIAN VAULT\Destino\PROCEDIMIENTOS\PROCEDIMIENTO - Deploy Blog y Wiki a Cloudflare Pages.md (esta nota) | Sammi (vault Obsidian principal del Escritorio) | Documentación canónica que lee el operador |
PoisonXploIT/wiki → content/PROCEDIMIENTOS/deploy-blog-wiki.md | Agentes que clonen wiki | Versión en el repo publicable (filtrada por ignorePatterns, no se publica en doc.sammideblas.com pero SÍ en GitHub) |
PoisonXploIT/blog → .github/DEPLOY.md | Agentes que clonen blog | Copia espejo del anterior |
PoisonXploIT/blog y PoisonXploIT/wiki → AGENTS.md (raíz) | Agentes (lo buscan por convención) | Puntero corto a los procedimientos + reglas |
Sincronización manual: si cambias algo aquí (vault del Escritorio), debes propagar a los otros 3 sitios a mano. Pendiente de crear un script que lo automatice.
13. Troubleshooting exhaustivo
13.1 Push rechazado por workflow scope
Síntoma:
! [remote rejected] main -> main (refusing to allow an OAuth App to create or update workflow
`.github/workflows/deploy.yml` without `workflow` scope)
error: failed to push some refs to 'https://github.com/PoisonXploIT/<repo>.git'
Causa: el token OAuth de gh no tiene scope workflow (necesario para pushear commits que tocan .github/workflows/*.yml).
Fix:
gh auth refresh -h github.com -s workflow
gh auth status # verificar 'workflow' en los scopes
# reintentar el push13.2 Workflow success pero el sitio no se actualiza
Causa posible 1: workflow disparado desde una rama que no es main (no production branch). Comprobar el on.push.branches del deploy.yml.
Causa posible 2: --project-name erróneo en el workflow. Verificar que coincide exactamente con el nombre del CF Pages project (blog o sammideblas-wiki).
Causa posible 3: el dominio custom está apuntando a otro deployment. Ver dashboard de CF Pages → proyecto → “Deployments” → mirar cuál es el “Production” más reciente.
13.3 npm ci falla en Actions con lockfile out of sync
Causa: package-lock.json no se commiteó al cambiar dependencias.
Fix local:
cd <blog-build|wiki-build>
npm install # actualiza package-lock.json
git add package.json package-lock.json
git commit -m "chore(deps): <descripcion>"
git push origin main13.4 Workflow falla en Build Quartz con error TypeScript
Probablemente un typo en quartz.config.ts o quartz.layout.ts. Ver logs del step: gh run view <id> --repo <repo> --log-failed.
En local, reproducir con npx quartz build y arreglar. También sirve para validar antes de pushear:
npx tsc --noEmit # typecheck
npx prettier . --check # formato
# si pasa, deberia desplegar bien13.5 Deploy Cloudflare falla con Authentication error
Causa: secret CLOUDFLARE_API_TOKEN caducado o con permisos insuficientes.
Fix:
- Regenerar token (sección 5.1).
gh secret set CLOUDFLARE_API_TOKEN --repo <repo> --body "<nuevo-token>".- Re-ejecutar el workflow desde la web: Actions → run → “Re-run failed jobs” o
gh run rerun <id> --repo <repo> --failed.
13.6 Push falla con Repository not found
Causa posible 1: el remote está apuntando a un repo que no existe (sucedió con el blog antes del 2026-07-06, cuando PoisonXploIT/blog no estaba creado).
Fix:
gh repo create PoisonXploIT/blog --private
git remote set-url origin https://github.com/PoisonXploIT/blog.git
git push -u origin mainCausa posible 2: no tienes permisos en el repo. gh auth status y verificar el user.
13.7 Imágenes externas no cargan en el sitio publicado
Causa probable: CSP (Content Security Policy) restrictiva en quartz.config.ts que bloquea el dominio de la imagen.
Las imágenes en https://blogger.googleusercontent.com/... ya están permitidas por commits previos (13c0d53, afbe80f). Si añades imágenes desde un dominio nuevo, hay que:
- Editar
quartz.config.ts→ sección CSP → añadir el dominio. - Valida los permisos de la rama de imágenes (p. ej.
img-src).
13.8 Nota nueva en content/PROCEDIMIENTOS/ no aparece en doc.sammideblas.com
Esperado. La carpeta content/PROCEDIMIENTOS/ está en ignorePatterns de quartz.config.ts → no se compila → no se publica. Pero SÍ se commitea al repo y se sincroniza con todas las máquinas vía git. That’s by design.
13.9 La página de un post devuelve 404
Podría ser:
- El
draft: trueen frontmatter (filtrado por Quartz). - Slug mal calculado (caracteres raros, espacios). El archivo debe llamarse
0xNN-slug-con-guiones.md(sin espacios, sin acentos). - El post no está commiteado o el deploy aún está corriendo.
Verificar:
gh run list --repo PoisonXploIT/blog --limit 1
# esperarse al "completed / success" antes de checkear el URL
# (la URL aparece en el último run: gh run view <id> ... --log)13.10 El wiki se publica con notas internas que no deberían salir
Causa: añadiste una carpeta sensible y no la metiste en ignorePatterns. Editar quartz.config.ts:
ignorePatterns: [..., "NUEVA_CARPETA", "NUEVA_CARPETA/*"],Hacer commit + push. El próximo deploy la filtra.
13.11 Cambié algo en quartz.config.ts y se rompe el build
Quartz es sensible a la sintaxis TypeScript/JSX. Verificar siempre en local antes de pushear:
npx tsc --noEmit # typecheck
npx quartz build # dry-run build13.12 Cloudflare falla con wrangler-action@v3 “could not authenticate”
El secret CLOUDFLARE_ACCOUNT_ID no está fijado o su valor tiene caracteres extra. Verificar:
gh secret list --repo <repo>Si falta, re-crearlo (sección 5.2). El valor 43c5d4a754f2951ba36185df5deef60c debe ir sin comillas ni espacios.
13.13 Build de Quartz muy lento en CI (>10 min)
Probablemente muchas notas o notas muy grandes en el wiki. Cosas a probar:
npx quartz build -d docspara ver logs verbosos.- Limitar
--concurrencysegún los recursos. - No suele ser un problema; la wiki tarda ~3-8 min con ~250 notas.
13.14 Privacy: token pegado accidentalmente en chat/log
Acción inmediata:
- Dashboard Cloudflare → lista de tokens → sobre el token comprometido → Roll (regenera valor) o Delete.
- Si lo deleteas, crear uno nuevo (sección 5.1).
gh secret set CLOUDFLARE_API_TOKEN --repo PoisonXploIT/blog --body "<nuevo>"gh secret set CLOUDFLARE_API_TOKEN --repo PoisonXploIT/wiki --body "<nuevo>"- Re-ejecutar workflows para confirmar.
Regla de oro: nunca pegues el token en chat, commits, ni archivos del repo/vault. Los secretos viven en GitHub Secrets, no en texto plano en ninguna parte.
14. Historia (contexto para agentes futuros)
Antes del 2026-07-06:
- El blog no tenía repo GitHub (
PoisonXploIT/blogdevolvía 404). Solo existía localmente en la máquina principal. - El deploy del blog era manual con
npx wrangler pages deploy public --project-name=blogdesde un host Linux en/opt/data/blog-build. El token OAuth de wrangler vivía en~/.config/.wranglerde ese Linux (NO se sincronizaba con el mirror Windowshermes_data). - El workflow del wiki (
deploy.yml) estaba numéricamente apuntando a GitHub Pages (conactions/deploy-pages@v4) en vez de Cloudflare Pages. GitHub Pages no estaba activado en el repo (gh api .../pagesdevolvía 404). El deploy real del wiki también era manual con wrangler. - Ningún
AGENTS.mdexistía. - Una nueva máquina nunca podía encontrar ni la fuente ni el flujo de deploy sin preguntar.
Cambio del 2026-07-06 (motivado por el deploy del post 0x70):
- Tras descubrir (vía logs en
~/.config/.wrangler/logs/) que el deploy real era wrangler-mánual y que Cloudflare Account ID era43c5d4a754f2951ba36185df5deef60c, se ejecutó un deploy manual del post 0x70 (commit9aa5aee) desde Windows → deployment508eb468.blog-f5e.pages.devlive en sammideblas.com. - Se detectó que el remote
PoisonXploIT/blogera 404 →gh repo create PoisonXploIT/blog --private. - Se unificó el flujo: GitHub Actions
deploy.ymlconcloudflare/wrangler-action@v3en ambos repos, secretsCLOUDFLARE_API_TOKEN+CLOUDFLARE_ACCOUNT_IDen cada repo. - Para el wiki, el
deploy.ymlse reescribió de GitHub Pages → Cloudflare Pages. - Se amplió el scope OAuth de
ghconworkflow(gh auth refresh -h github.com -s workflow) para poder pushear los commits que reescribíandeploy.yml. - Push inicial del blog: 67 commits acumulados + post 0x70 + workflow nuevo → workflow success (run
28812468087, 57s). - Push del wiki: commit
de93600con workflow nuevo → workflow success (run28812453786). - Se creó la documentación:
PoisonXploIT/wiki→content/PROCEDIMIENTOS/deploy-blog-wiki.md(versionada, filtrada del build público porignorePatterns).- Se quitó la regla
content/PROCEDIMIENTO*del.gitignoredel wiki para que esa carpeta sí se versionee. PoisonXploIT/blog→.github/DEPLOY.md(copia espejo).AGENTS.mden ambos repos (puntero corto, reglas críticas).- Esta nota en el vault Obsidian principal del Escritorio (copia canónica para el operador).
A partir de esa fecha:
- Deploy manual con wrangler ya NO es necesario para producción.
- Cualquier máquina puede desplegar con
git clone+git push. - Cualquier agente IA encuentra el procedimiento leyendo
AGENTS.md+DEPLOY.md(blog) ocontent/PROCEDIMIENTOS/deploy-blog-wiki.md(wiki), sin preguntar.
15. Comandos rápidos (cheatsheet)
# === BLOG ===
cd C:\Users\Sammi De Blas\hermes_data\blog-build
npx quartz build --serve # preview local
git add content\posts\0xNN-slug.md
git commit -m "post: 0xNN Título"
git push origin main # despliega solo
gh run list --repo PoisonXploIT/blog --limit 1 # estado del deploy
# === WIKI ===
cd C:\Users\Sammi De Blas\hermes_data\wiki-build
git add content\<ruta>
git commit -m "wiki: descripción"
git push origin main # despliega solo
gh run list --repo PoisonXploIT/wiki --limit 1
# === Secrets (solo si caduca el token) ===
$env:CLOUDFLARE_API_TOKEN = "<token>"
gh secret set CLOUDFLARE_API_TOKEN --repo PoisonXploIT/blog --body $env:CLOUDFLARE_API_TOKEN
gh secret set CLOUDFLARE_ACCOUNT_ID --repo PoisonXploIT/blog --body "43c5d4a754f2951ba36185df5deef60c"
gh secret set CLOUDFLARE_API_TOKEN --repo PoisonXploIT/wiki --body $env:CLOUDFLARE_API_TOKEN
gh secret set CLOUDFLARE_ACCOUNT_ID --repo PoisonXploIT/wiki --body "43c5d4a754f2951ba36185df5deef60c"
Remove-Item Env:\CLOUDFLARE_API_TOKEN
# === Deploy local manual (alternativa, NO usar de rutina) ===
$env:CLOUDFLARE_API_TOKEN = "<token>"
$env:CLOUDFLARE_ACCOUNT_ID = "43c5d4a754f2951ba36185df5deef60c"
npx quartz build
npx wrangler@latest pages deploy public --project-name=blog --commit-dirty=true # blog
npx wrangler@latest pages deploy public --project-name=sammideblas-wiki --commit-dirty=true # wiki
# === Verificar deploys CF (opcional) ===
$acc = "43c5d4a754f2951ba36185df5deef60c"
Invoke-RestMethod "https://api.cloudflare.com/client/v4/accounts/$acc/pages/projects/blog/deployments?per_page=1" -Headers @{Authorization="Bearer $env:CLOUDFLARE_API_TOKEN"}
# === Auth gh (si caduca o falta scope workflow) ===
gh auth login
gh auth refresh -h github.com -s workflow
gh auth status16. Reglas de oro para agentes IA
- Antes de tocar workflows o publicar: lee
AGENTS.md+.github/DEPLOY.mddel repo en cuestión (o esta nota del vault del Escritorio, que es la versión canónica). - Tokens nunca en texto plano en commits, notas, chat, ni logs. Viven en GitHub Secrets (
gh secret set). Si los ves commiteados, bórralos inmediatamente y regenera. - Para desplegar, siempre que sea posible
git push origin main. El deploy manual con wrangler es solo backup. - El commit a
maindespliega solo — no ejecutar wrangler local sin razón (puede desincronizar CF y GitHub). - La carpeta
content/PROCEDIMIENTOS/del wiki se versiona pero NO se publica (filtrada porignorePatternsdequartz.config.ts). No reactivar la reglacontent/PROCEDIMIENTO*en.gitignore. - Numeración de posts: hex consecutivo (
0x71,0x72…). Slug en inglés con guiones (0x71-titulo-del-post.md). - Plantilla frontmatter siempre completa (
title,date,draft: false,description,tags). - Si un push falla por
workflowscope:gh auth refresh -h github.com -s workflow(es interactivo, abre navegador). - Si vas a añadir imagen desde dominio nuevo: revisar CSP en
quartz.config.ts(13c0d53yafbe80fson los commits que permitieron Blogger). - Verificar siempre el estado del workflow tras un push:
gh run list --repo PoisonXploIT/<repo> --limit 1. No asumir “Success” sin comprobar.
17. Apéndice: cómo reproducir toda la configuración desde cero
Si algún día hay que reconstruir la infraestructura (nueva cuenta CF, nuevo GitHub org, etc.) aquí están los pasos en orden.
17.1 Cloudflare
- Crear cuenta en https://dash.cloudflare.com.
- Workers & Pages → Create application → Pages → Upload assets → nomear proyecto
blog. Repetir consammideblas-wiki. - Custom domains en cada proyecto: configurar CNAMEs a
<project>.pages.devdesde el dashboard de DNS de Cloudflare parasammideblas.com,blog.sammideblas.com,www.sammideblas.com(blog) ydoc.sammideblas.com(wiki). - Profile → API Tokens → Create Token → Custom Token con
Account → Cloudflare Pages → Editpara esa cuenta. Copiar el valor.
17.2 GitHub
- Crear org
PoisonXploIT(o usuario). gh repo create PoisonXploIT/blog --privategh repo create PoisonXploIT/wiki --private- Fijar secrets en cada repo (ver sección 5.2).
17.3 Repositorios locales
git clone https://github.com/PoisonXploIT/blog.git <ruta>/blog-buildgit clone https://github.com/PoisonXploIT/wiki.git <ruta>/wiki-build- En cada uno: copiar/econtrar el contenido Quartz (
package.json,quartz.config.ts,quartz.layout.ts,content/,quartz/,static/…). ConfigurarbaseUrlenquartz.config.ts(sammideblas.comodoc.sammideblas.com). npm install.
17.4 Workflows
Crear .github/workflows/deploy.yml en cada repo (copiar de sección 7).
17.5 Documentación
AGENTS.mden cada repo (raíz)..github/DEPLOY.mden el blog (copia de la nota del wiki).content/PROCEDIMIENTOS/deploy-blog-wiki.mden el wiki.- Esta nota en el vault Obsidian del Escritorio.
17.6 Verificación final
# trigger manual del workflow desde la web de Actions ó:
gh workflow run deploy.yml --repo PoisonXploIT/blog
gh workflow run deploy.yml --repo PoisonXploIT/wiki
# esperar y visitar URLs publicas18. Metadatos de mantenimiento
- Última actualización canónica: 2026-07-06 (Pull del post 0x70)
- Autor: Sammi De Blas (con asistencia de agente IA glm-5.2 vía opencode)
- Ubicaciones espejo:
PoisonXploIT/wiki→content/PROCEDIMIENTOS/deploy-blog-wiki.mdPoisonXploIT/blog→.github/DEPLOY.mdPoisonXploIT/wikiyPoisonXploIT/blog→AGENTS.md
- Verificación de hashes (_COMMITS):
- Blog workflow unificado:
6747ae1 - Wiki workflow unificado:
de93600 - Wiki nota procedimiento:
0c47726 - Blog AGENTS+DEPLOY:
5a7e71d - Wiki AGENTS:
2e70a6e - Último deploy blog en production:
9aa5aee(post 0x70) → deployment508eb468.blog-f5e.pages.dev - Último deploy wiki en production:
95d3cf3e(antes del cambio de workflow)
- Blog workflow unificado:
EOF
“La verdad está ahí fuera. Y el aire está cargado de información.”