Nezzontli (del náhuatl: "mi camino") es mi sitio web personal: una bitácora técnica donde documento proyectos de tecnología ética, física aplicada, sistemas resilientes y automatización.
- 🌐 Clearnet: nezzontli.xyz
- 🧅 Tor:
http://4n3jao4wyhpbydns4edzuq7g4usjrrfpmpq3c6l3hlvqxkeskpt2olid.onion - 📬 Mastodon: @[email protected]
- 🐦 Codeberg: codeberg.org/Richard7987
| Capa | Tecnología |
|---|---|
| Generador de sitio | Zola v0.21.0 (Rust) |
| Tema base | Duckquill v6.3.0 |
| Estilos | SCSS compilado por Zola |
| Servidor | OpenBSD — Amsterdam |
| CI/CD | Forgejo Actions (Codeberg) |
| Analíticas | GoatCounter (nezzontli.goatcounter.com) |
| Comentarios | API de Mastodon (masto.es) |
El sitio es completamente estático — no hay base de datos, no hay servidor de aplicaciones, no hay dependencias de CDN externo. Todo el CSS, JS y fuentes se sirven desde el propio servidor.
.
├── content/ # Archivos Markdown (.md) con el contenido del sitio
│ ├── blog/ # Artículos técnicos y reflexiones
│ ├── photos/ # Álbumes fotográficos y galerías
│ └── about/ # Información personal y CV
├── static/ # Activos estáticos (imágenes, JS, fuentes)
├── templates/ # Plantillas Tera para la estructura HTML
├── sass/ # Archivos fuente de estilos SCSS
└── config.toml # Configuración principal del sitio y del tema
El sitio usa el motor de plantillas Tera (incluido en Zola), con herencia de bloques.
base.html
├── index.html → Homepage
├── article_list.html → /blog/
├── section.html → Secciones genéricas
├── page.html → Páginas sin sección especial
├── photo_gallery.html → /photos/
├── taxonomy_list.html → /tags/
├── taxonomy_single.html → /tags/<tag>/
├── 404.html → Error 404
└── article.html → Artículos del blog
└── photo_album.html → Álbumes de fotos
| Archivo | Función |
|---|---|
partials/head.html |
<head>: meta, CSP, CSS, JS, OG tags |
partials/articles.html |
Tarjetas de artículos del blog |
partials/photo_albums.html |
Grid de tarjetas de álbumes |
partials/comments.html |
Sistema de comentarios vía Mastodon |
partials/csp.html |
Genera el header CSP desde config.toml |
partials/variables.html |
Inyecta --accent-color como CSS variable inline |
macros/translate.html |
Internacionalización (i18n) |
index.html carga las secciones blog y photos en tiempo de build con get_section() y muestra el artículo y álbum más recientes. No requiere JavaScript.
Los íconos son SVG embebidos como data URIs en variables CSS (--icon-next, --icon-home, etc.) definidas en sass/icons.scss. Se renderizan con mask-image + background-color: currentColor, heredando el color del texto y funcionando en dark/light mode sin código adicional. No se usa ninguna fuente de íconos externa.
| Clave | Valor | Descripción |
|---|---|---|
base_url |
https://nezzontli.xyz |
URL base del sitio |
default_language |
en |
Idioma por defecto |
compile_sass |
true |
Zola compila SCSS automáticamente |
minify_html |
true |
HTML minificado en producción |
generate_feeds |
true |
Genera rss.xml y atom.xml |
extra.default_theme |
dark |
Tema visual por defecto |
extra.accent_color |
#ff7800 |
Color de acento (modo claro) |
extra.accent_color_dark |
#ffa348 |
Color de acento (modo oscuro) |
extra.emoji_favicon |
∑⍼ |
Favicon generado desde emoji |
extra.show_copy_button |
true |
Botón "copiar" en bloques de código |
El CSP se genera dinámicamente desde config.toml y se inyecta en el <head> de cada página via partials/csp.html. Las directivas clave:
script-src 'self'— solo scripts locales, ningún inline ni CDN externoconnect-src https://masto.es https://nezzontli.goatcounter.com— necesario para comentarios y analíticasframe-src https://player.vimeo.com https://www.youtube-nocookie.com— para embeds de video
Los comentarios se activan enlazando un toot a un artículo. Se configuran en el front matter de cada post:
[extra.comments]
id = "ID_DEL_TOOT"comments.js llama a https://masto.es/api/v1/statuses/<id>/context solo cuando el usuario hace clic en "cargar comentarios" — no en el primer render.
Analíticas sin cookies servidas desde nezzontli.goatcounter.com. El script count.js se sirve localmente — no se carga nada desde servidores de terceros.
Todos los scripts son locales, cargados con defer.
| Archivo | Cuándo carga | Qué hace |
|---|---|---|
closable.js |
Siempre | Cierra elementos con atributo data-closable |
copy-button.js |
Si show_copy_button = true |
Botón "copiar" en bloques de código |
count.js |
Si extra.goatcounter configurado |
Envía visita a GoatCounter |
comments.js |
Si page.extra.comments.id existe |
Carga comentarios desde la API de Mastodon |
photo-album.js |
En plantilla photo_album.html |
Lightbox al hacer clic en fotos |
autofit-crt.js |
Si page/section.extra.crt = true |
Escala font-size del bloque CRT en pantallas pequeñas |
Para activar autofit-crt.js en una página agregar al front matter:
[extra]
crt = truecontent/blog/mi-articulo/index.md
+++
title = "Título"
date = 2025-01-15
description = "Descripción breve."
authors = ["Alejandro B.E."]
[taxonomies]
tags = ["tag1", "tag2"]
[extra]
banner = "imagen.jpg" # imagen de portada (relativa al artículo)
toc = true # tabla de contenidos
# Comentarios: ID del toot de Mastodon
# [extra.comments]
# id = "ID_DEL_TOOT"
+++content/photos/mi-album/index.md
+++
title = "Nombre del álbum"
date = 2025-01-15
description = "Descripción del álbum."
[taxonomies]
tags = ["viaje"]
[extra]
cover_image = "/images/mi-album/portada.jpg"
+++Las imágenes se guardan en static/images/<nombre-album>/.
| Shortcode | Uso |
|---|---|
alert |
Callout de tip, warning, etc. |
crt |
Bloque con efecto terminal CRT |
image |
Imagen con caption |
youtube |
Embed YouTube (privacy mode) |
vimeo |
Embed Vimeo |
video |
Video HTML5 local |
mastodon |
Embed de toot |
{% alert(tip=true) %}Texto del tip.{% end %}
{% crt() %}
ASCII art
{% end %}
{% image(src="foto.jpg", alt="Descripción", caption="Pie de foto") %}
{% youtube(id="VIDEO_ID") %}
El sitio se compila y sirve desde un servidor OpenBSD en Amsterdam. El repositorio vive en Codeberg y usa Forgejo Actions para el pipeline CI/CD.
En push a main (.forgejo/workflows/deploy.yml):
- Checkout del repositorio
- Build con Zola (
zola build) — genera/public - Deploy al servidor
En pull request (.forgejo/workflows/preview.yml):
- Checkout del repositorio
- Build con Zola
- Deploy como preview
El servidor también expone el sitio en la red Tor. Los navegadores compatibles (Tor Browser, Brave) detectan el mirror automáticamente mediante:
<meta http-equiv="onion-location" content="http://4n3jao4wyhpbydns4edzuq7g4usjrrfpmpq3c6l3hlvqxkeskpt2olid.onion">El mirror sirve todos los assets (CSS, JS, fuentes) localmente para funcionar sin depender de la clearnet.
- Zola v0.21.0+
# Clonar
git clone https://codeberg.org/Richard7987/wesite.git
cd wesite
# Servidor de desarrollo con live reload
zola serve
# → http://localhost:1111
# Build de producción
zola build
# → genera /public/
# Verificar links internos
zola check- Sin cookies — GoatCounter no usa cookies
- Sin CDN externo — fuentes, JS y CSS se sirven localmente
- Sin rastreadores de terceros — sin Google Analytics ni similares
- Mirror Tor — accesible desde la red Tor
- CSP estricto — previene carga de recursos no autorizados
- HSTS — fuerza HTTPS con preload de 2 años
Este proyecto usa licencias distintas según el tipo de contenido:
| Qué | Licencia |
|---|---|
| Código (templates, SCSS, JS, config) | MIT — libre para usar, modificar y redistribuir |
Posts y artículos (content/) |
CC BY-NC-ND 4.0 — atribución requerida, sin uso comercial, sin derivados |
Fotografías (static/images/) |
CC BY-NC-ND 4.0 — atribución requerida, sin uso comercial, sin derivados |
El tema base Duckquill es © David "Daudix" Lapshin, licenciado bajo MIT — su aviso de copyright se conserva en LICENSE.txt según lo requiere dicha licencia.