Imagen representativa de la guía

doc-guia

Configuración paso a paso de Syncthing, Obsidian, Templater, automatización, launchd y notas desde Android.

15 de junio de 2026, 00:00 15 de junio de 2026, 00:00 Admin, Ernesto Uriszar
documentacion
guiaconfiguracionsyncthingobsidiantemplaterlaunchd

guia — MarkUP

Última revisión: 2026-06-15 Propósito: Explicar cómo configurar cada pieza del sistema para que funcione correctamente.


1. Syncthing (sincronización Android ↔ Mac)

Syncthing sincroniza la carpeta src/content/ entre Android y Mac. No usa ningún servicio en la nube — es peer-to-peer (los archivos viajan directamente entre dispositivos).

¿Por qué Syncthing y no otra cosa?

1.1 Configuración en Mac

La app Syncthing para Mac se descarga desde syncthing.net. Una vez instalada:

ParámetroValor
Ruta compartida/Users/ernestouriszar/markeup/src/content
Tipo de carpetaEnviar y Recibir
VersionadoSimple (5 versiones) — guarda copias de archivos modificados
Intervalo de escaneo60 segundos — cada minuto revisa si hay cambios
Ignorar permisosActivado — evita problemas de permisos entre Android y Mac
Patrones ignorados.stversions/, *.sync-conflict*, .DS_Store

Cómo añadir la carpeta:

  1. Abre Syncthing en Mac
  2. Haz clic en “Add Folder”
  3. Selecciona la ruta /Users/ernestouriszar/markeup/src/content
  4. Configura los parámetros como se indica arriba
  5. Acepta

1.2 Configuración en Android

La app Syncthing para Android se descarga desde F-Droid (recomendado) o Google Play.

ParámetroValor
Ruta destino/storage/emulated/0/Obsidian/MiVault
Tipo de carpetaEnviar y Recibir
Ignorar permisosActivado
Patrones ignoradosmismos que en Mac

Cómo añadir la carpeta:

  1. Abre Syncthing en Android
  2. Menú (3 barras) → “Folders” → ”+” (añadir carpeta)
  3. Selecciona la ruta /storage/emulated/0/Obsidian/MiVault
  4. Configura los parámetros como se indica arriba
  5. Espera a que aparezca el Mac como dispositivo y vincúlalo

1.3 Tiempos de sincronización

Si un archivo tarda más de 10 segundos, algo está mal. Verifica que ambos dispositivos estén en la misma red y que Syncthing esté corriendo en ambos.

1.4 Operaciones comunes

Pausar/reanudar la sincronización desde Android (útil si estás editando y no quieres que se sincronice hasta que termines):

  1. Abrir Syncthing en Android
  2. Tocar el dispositivo Mac en la lista
  3. Alternar el interruptor de pausa

Forzar sincronización inmediata (si no quieres esperar los 60s de escaneo):

  1. Syncthing → menú (3 puntos) → “Rescan all folders”

1.5 Solución de problemas comunes de Syncthing

ProblemaCausa probableSolución
Los dispositivos no se venFirewall o redes diferentesVerificar que ambos estén en la misma WiFi. En Mac, ir a Preferencias del Sistema → Firewall → Permitir Syncthing
Sincronización muy lentaArchivos grandes o red congestionadaLas imágenes WebP suelen pesar <100KB, deberían ser rápidas
Archivos .sync-conflict apareciendoEl mismo archivo se editó en ambos lados casi simultáneamenteEliminar los conflictos (el .gitignore ya los ignora). Revisar qué versión mantener
La carpeta no se sincronizaID de dispositivo incorrectoVerificar que el ID del Mac esté añadido en Android y viceversa

2. Obsidian y Templater

Obsidian es el editor de notas. Se usa tanto en Android (para escribir) como en Mac (para revisar). Templater es un plugin que permite crear plantillas con lógica programática (JavaScript).

2.1 Instalación de Templater

  1. Abre Obsidian
  2. Configuración → Community plugins → “Browse”
  3. Busca “Templater” e instálalo
  4. Actívalo en “Community plugins”

2.2 Estructura del vault en Android

/storage/emulated/0/Obsidian/MiVault/
├── posts/       ← Notas del blog (se crean aquí mediante la plantilla)
└── templates/   ← Plantillas de Templater (debe coincidir con la configuración)

2.3 Configuración de Templater

En Mac y Android (ambos deben tener Templater instalado y configurado igual):

ParámetroValorPor qué
Template folder locationtemplates/Aquí se guarda la plantilla template-sin-preguntas.md
Trigger Templater on new file creationActivadoPara que al crear un archivo desde plantilla se procese automáticamente

2.4 La plantilla: template-sin-preguntas.md

Solo existe una plantilla en el sistema. Su función es:

  1. Recibir el título que el usuario escribe
  2. Generar un slug válido (sin acentos, sin espacios, sin caracteres especiales)
  3. Crear la fecha actual en formato ISO
  4. Mover el archivo a la carpeta posts/ (esto es crítico)
  5. Rellenar el frontmatter con valores por defecto

Archivo: src/content/templates/template-sin-preguntas.md

<%*
const title = tp.file.title;

const slug = title
  .toLowerCase()
  .normalize("NFD")
  .replace(/[\u0300-\u036f]/g, "")
  .replace(/[^\w\s-]/g, "")
  .trim()
  .replace(/\s+/g, "-");

const today = tp.date.now("YYYY-MM-DDTHH:mm:ss") + ".000Z";

// ─── MOVER A CARPETA posts ─────────────────────────────────────────────────
await tp.file.move(`/posts/${slug}`);

_%>
---
title: '<% title %>'
slug: <% slug %>
description: ''
type: post
lang: es-ES
draft: true
authors:
  - Admin
  - Ernesto Uriszar
pubDate: <% today %>
updatedDate: <% today %>
categories:
  - sin-categoría
tags:
  - tag-1
featured: false
allowComments: true
share: true
image: '/img/<% slug %>.webp'
imageAlt: ''
canonicalUrl: ''
aliases:
  - '<% title %>'
  - <% slug %>
noIndex: false
hideTOC: false
showTOC: false
hideCoverImage: false
---

## H2
Párrafo

## Referencias

### Bibliografía Fundamental

- Autor, A. (Año). _Título_. Editorial.
- Autor, A. (Año). _Título_. Editorial.
- Autor, A. (Año). _Título_. Editorial.

### Artículos y Fuentes Web

- [Título](URL) — Descripción breve
- [Título](URL) — Descripción breve
- [Título](URL) — Descripción breve

### Videos Recomendados

- [Título](URL) — Duración · Por qué verlo
- [Título](URL) — Duración · Por qué verlo
- [Título](URL) — Duración · Por qué verlo

### Personas Clave

- **Nombre** — [Wikipedia](URL) — Rol breve
- **Nombre** — [Wikipedia](URL) — Rol breve
- **Nombre** — [Wikipedia](URL) — Rol breve

### Organizaciones Relevantes

- **Nombre** — Qué hace
- **Nombre** — Qué hace

---

> _Nota final (30-50 palabras)._

2.5 La línea crítica: tp.file.move() explicada en detalle

await tp.file.move(`/posts/${slug}`);

¿Qué hace exactamente? Cuando creas un archivo desde Templater, el archivo se guarda inicialmente donde tú estés en Obsidian. Esta línea mueve el archivo a la subcarpeta posts/ y lo renombra con el slug.

¿Por qué es necesaria? Porque Astro espera que los posts estén dentro de src/content/posts/. Si creas un archivo en la raíz de src/content/ (como pasó con Prueba.md), Astro lanza el error:

Collection entries must live in the “posts” collection subdirectory

Sin tp.file.move(), cada nota que crees terminará en la raíz de src/content/ y romperá el build.

¿Qué pasa si olvido incluir esta línea? El archivo se crea en la carpeta actual de Obsidian, no en posts/. Cuando Syncthing lo sincronice, terminará en src/content/ (raíz) y el build fallará.

2.6 Cómo crear una nota nueva paso a paso

  1. Abre Obsidian en Android
  2. Abre la paleta de comandos: Ctrl+P (en Android) o Cmd+P (en Mac)
  3. Selecciona: “Templater: Create new note from template”
  4. Elige la plantilla: template-sin-preguntas.md
  5. Escribe el título: Por ejemplo, “Mi nuevo artículo”
  6. Templater genera automáticamente:
    • El slug: mi-nuevo-articulo
    • La fecha actual
    • El frontmatter básico
    • Mueve el archivo a posts/mi-nuevo-articulo.md
  7. Rellena el frontmatter:
    • description: Un resumen breve del artículo (aparece en las cards y en SEO)
    • categories: Cambia de sin-categoría a la categoría correcta (ej: biodanza, tecnologia, literatura)
    • tags: Añade tags relevantes (ej: meditacion, conciencia)
    • image: Cambia la ruta si tienes una imagen específica
    • draft: Cambia a false cuando el artículo esté listo para publicar
  8. Rellena el contenido siguiendo la estructura de la plantilla
  9. Guarda el archivo (normalmente Obsidian guarda automáticamente)

3. Automatización (watchexec + launchd + auto-commit.sh)

3.1 ¿Cómo funciona la automatización?

Cada vez que Syncthing recibe un archivo nuevo o modificado en src/content/:

  1. watchexec detecta el cambio (usa fsevents, la API nativa de macOS para monitorizar archivos)
  2. watchexec ejecuta auto-commit.sh
  3. auto-commit.sh añade los cambios a git, hace commit y push
  4. GitHub Actions recibe el push y ejecuta el build

3.2 Contenido exacto de auto-commit.sh

#!/bin/bash
cd /Users/ernestouriszar/markeup || exit 1

# Añadir TODO lo que haya cambiado en src/content
git add src/content/

# Si hay cambios, commit y push
if ! git diff --cached --quiet; then
    git commit -m "📱 Auto-sync from Android $(date '+%Y-%m-%d %H:%M:%S')"
    git push origin main
fi

Explicación línea por línea:

¿Por qué se añade src/content/ entero y no archivos específicos? Porque queremos capturar cualquier cambio: archivos nuevos, modificados, eliminados, renombrados, imágenes, etc. Es más simple y seguro que añadir archivos uno por uno.

3.3 Gestión de watchexec con launchd (servicio persistente)

Inicialmente, watchexec se ejecutaba con nohup en segundo plano. Pero esto tenía un problema: al reiniciar el Mac, el proceso moría. Para solucionarlo, se creó un servicio launchd que:

3.3.1 El archivo de configuración launchd

Para que launchd gestione watchexec, necesitas un archivo .plist en ~/Library/LaunchAgents/.

Crear el archivo:

cat > ~/Library/LaunchAgents/com.user.watchexec.plist << 'PLIST'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.user.watchexec</string>
    <key>ProgramArguments</key>
    <array>
        <string>/opt/homebrew/bin/watchexec</string>
        <string>-w</string>
        <string>/Users/ernestouriszar/markeup/src/content</string>
        <string>--</string>
        <string>/Users/ernestouriszar/markeup/auto-commit.sh</string>
    </array>
    <key>WorkingDirectory</key>
    <string>/Users/ernestouriszar/markeup</string>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/tmp/watchexec.log</string>
    <key>StandardErrorPath</key>
    <string>/tmp/watchexec.err</string>
</dict>
</plist>
PLIST

Explicación de las opciones:

3.3.2 Cargar el servicio

# Cargar el servicio (arranca watchexec ahora y en cada inicio de sesión)
launchctl load ~/Library/LaunchAgents/com.user.watchexec.plist

Explicación: launchctl load le dice a launchd que registre el servicio. La primera vez arranca watchexec inmediatamente. En adelante, arrancará automáticamente al iniciar sesión.

3.3.3 Verificar que el servicio está corriendo

launchctl list | grep watchexec

Explicación: launchctl list muestra todos los servicios gestionados por launchd. Filtrando por grep watchexec vemos solo nuestro servicio.

Salida esperada:

PID	Status	Label
1234	0	com.user.watchexec

3.3.4 Ver los logs si algo falla

# Ver errores
cat /tmp/watchexec.err

# Ver salida estándar (actividad normal)
cat /tmp/watchexec.log

Explicación: Si watchexec falla por alguna razón, el error se escribe en /tmp/watchexec.err. Si todo funciona bien, los mensajes de watchexec van a /tmp/watchexec.log.

3.3.5 Reiniciar el servicio

launchctl stop com.user.watchexec
launchctl start com.user.watchexec

Explicación: stop detiene temporalmente el proceso (launchd lo recordará y lo reiniciará si KeepAlive es true). start lo arranca inmediatamente. Útil si necesitas recargar la configuración o después de cambiar el .plist.

3.3.6 Detener el servicio (si alguna vez quieres pausar la automatización)

# Descargar el servicio (lo detiene y elimina del registro de launchd)
launchctl unload ~/Library/LaunchAgents/com.user.watchexec.plist

Explicación: unload elimina el servicio del registro de launchd. No se ejecutará más hasta que se vuelva a cargar con launchctl load. Útil si estás haciendo mantenimiento y no quieres que watchexec interfiera.

3.3.7 Reactivar el servicio

launchctl load ~/Library/LaunchAgents/com.user.watchexec.plist

Explicación: Vuelve a registrar el servicio en launchd. watchexec arranca inmediatamente.

3.4 Comparativa: nohup vs launchd

Aspectonohup (antiguo)launchd (nuevo)
Persiste tras reinicio❌ No✅ Sí
Se reinicia si falla❌ No✅ Sí
Fácil de verificar`ps auxgrep watchexec`
LogsEn nohup.out (poco manejable)En /tmp/watchexec.log y .err
InstalaciónUn comandoCrear .plist + launchctl load
PortabilidadFunciona en cualquier UNIXSolo macOS

3.5 Qué eventos detecta watchexec

TODO cambio en src/content/:

¿Qué NO detecta? Cambios fuera de src/content/. Si modificas package.json o astro.config.mjs, no se hará auto-commit (y es correcto, esos cambios deben hacerse manualmente).


4. Estructura de contenido

4.1 Lo que funciona (la estructura actual)

src/content/
├── posts/               ← ✅ DONDE DEBEN ESTAR las notas
│   ├── Biodanza/        ← 12 posts + 4 subposts (cuatro-elementos)
│   ├── Literatura/      ← 2 posts (Fernando Pessoa)
│   ├── documentacion/   ← 5 archivos de documentación del sistema
│   ├── img/             ← Imágenes originales (no referenciadas directamente)
│   ├── sample-folder-based-post/  ← Post demo con imágenes adjuntas
│   ├── *.md             ← 7 posts de tecnología
│   ├── Prueba.md        ← Post de prueba (borrador, draft: true)
│   └── Caca.md          ← ⚠️ Post de prueba (draft: false — se publicó)

4.2 Lo que NO funciona y por qué

4.3 Categorías existentes

Las categorías actuales son:

Si tu post no encaja en ninguna, usa sin-categoría temporalmente y luego crea una nueva categoría editando el frontmatter.


5. Flujo completo paso a paso (resumen)

Paso 1: Android — Obsidian guarda nota (con Templater)
  ↓ (1-5s) — Syncthing transfiere
Paso 2: Mac — Syncthing recibe archivo en src/content/posts/
  ↓ (inmediato) — watchexec (gestionado por launchd) detecta
Paso 3: Mac — auto-commit.sh: git add → commit → push
  ↓ (~5-15s según internet)
Paso 4: GitHub — Actions se dispara: npm run build
  ↓ (~30-60s) — Astro genera HTML, Pagefind indexa
Paso 5: Cloudflare — Recibe build y despliega en CDN
  ↓ (~30-60s)
🚀 SITIO ACTUALIZADO (total: 2-5 minutos)

6. Errores encontrados y soluciones

Error 1: Zod — frontmatter inválido

Síntoma: Al hacer npm run build, aparece ZodError: Invalid frontmatter.

Causa: Un post tiene frontmatter incompleto o mal formado (falta algún campo requerido).

Solución: Revisar el archivo que menciona el error y asegurarse de que tiene todos los campos requeridos: title, slug, description, pubDate, categories (mínimo 1), draft (opcional, default false).

Error 2: “must live in content collection”

Síntoma: Collection entries must live in the "posts" collection subdirectory.

Causa: Un archivo .md se creó en src/content/ (raíz) en lugar de src/content/posts/.

Solución: Verificar que la plantilla de Templater tenga tp.file.move(). Mover manualmente el archivo a posts/.

Error 3: Template parsing — ## const today

Síntoma: Templater no procesaba la plantilla correctamente y dejaba el código <%* ... %> visible.

Causa: La línea const today = ... estaba precedida de ## (formato markdown incorrecto dentro del bloque Templater).

Solución: La línea correcta es const today = tp.date.now("YYYY-MM-DDTHH:mm:ss") + ".000Z"; (sin ##).

Error 4: Archivos .stversions/ en git

Síntoma: Archivos de versiones de Syncthing apareciendo en git status.

Causa: Syncthing guarda versiones antiguas de archivos modificados en .stversions/.

Solución: Se añadieron al .gitignore:

.stfolder/
.stignore
.stversions/
.sync-conflict*