mirror of
https://github.com/skoelle/moonweb-site.git
synced 2026-09-17 17:00:25 +00:00
initial commit
This commit is contained in:
@@ -0,0 +1,125 @@
|
|||||||
|
name: Build & Deploy moonweb-site
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
# Cancel in-progress runs for the same branch
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
NODE_VERSION: "22"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build ${{ matrix.site }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
site: [hub, infra, smarthome, code, retro]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
|
cache: "npm"
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Build ${{ matrix.site }}
|
||||||
|
run: npm run build:${{ matrix.site }}
|
||||||
|
|
||||||
|
- name: Validate build output
|
||||||
|
run: |
|
||||||
|
if [ ! -d "dist/${{ matrix.site }}" ]; then
|
||||||
|
echo "Error: dist/${{ matrix.site }} not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ -z "$(ls -A dist/${{ matrix.site }}" 2>/dev/null)" ]; then
|
||||||
|
echo "Error: dist/${{ matrix.site }} is empty"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Build output:"
|
||||||
|
find dist/${{ matrix.site }} -type f | head -20
|
||||||
|
|
||||||
|
- name: Upload build artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: dist-${{ matrix.site }}
|
||||||
|
path: dist/${{ matrix.site }}
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
name: Deploy ${{ matrix.site }}
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- site: hub
|
||||||
|
cf_project: moonweb-hub
|
||||||
|
- site: infra
|
||||||
|
cf_project: moonweb-infra
|
||||||
|
- site: smarthome
|
||||||
|
cf_project: moonweb-smarthome
|
||||||
|
- site: code
|
||||||
|
cf_project: moonweb-code
|
||||||
|
- site: retro
|
||||||
|
cf_project: moonweb-retro
|
||||||
|
steps:
|
||||||
|
- name: Download build artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: dist-${{ matrix.site }}
|
||||||
|
path: dist/${{ matrix.site }}
|
||||||
|
|
||||||
|
- name: Deploy to Cloudflare Pages
|
||||||
|
uses: cloudflare/wrangler-action@v3
|
||||||
|
with:
|
||||||
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
|
command: pages deploy dist/${{ matrix.site }} --project-name=${{ matrix.cf_project }} --commit-dirty=true
|
||||||
|
|
||||||
|
summary:
|
||||||
|
name: Deploy Summary
|
||||||
|
needs: deploy
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: always()
|
||||||
|
steps:
|
||||||
|
- name: Print deployment summary
|
||||||
|
run: |
|
||||||
|
echo "## Deployment Summary" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
if [ "${{ needs.deploy.result }}" = "success" ]; then
|
||||||
|
echo "✅ All sites deployed successfully!" >> $GITHUB_STEP_SUMMARY
|
||||||
|
else
|
||||||
|
echo "❌ Deployment failed. Check the deploy jobs for details." >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "### Sites" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| Site | URL |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "|------|-----|" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| hub | https://hub.moonweb.org |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| infra | https://infra.moonweb.org |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| smarthome | https://smarthome.moonweb.org |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| code | https://code.moonweb.org |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| retro | https://retro.moonweb.org |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
# Required repo secrets:
|
||||||
|
# CLOUDFLARE_API_TOKEN — Cloudflare API token with Pages:Edit permission
|
||||||
|
# CLOUDFLARE_ACCOUNT_ID — Cloudflare account ID
|
||||||
|
#
|
||||||
|
# Required one-time setup (see PLAN.md Phase 2):
|
||||||
|
# Create Cloudflare Pages projects and bind custom domains.
|
||||||
|
# DNS is already on Cloudflare (SPEC.md §4.6).
|
||||||
|
# Note: Cloudflare Free-Tier limits to 5 Pages projects per repository.
|
||||||
|
#
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
.cache/
|
||||||
|
*.log
|
||||||
|
.idea/
|
||||||
|
.env*
|
||||||
|
*.pem
|
||||||
|
*.key
|
||||||
|
*.p12
|
||||||
|
*.pfx
|
||||||
|
*.jks
|
||||||
|
*.cert
|
||||||
|
*.crt
|
||||||
|
.DS_Store
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
# AGENTS.md — moonweb-site
|
||||||
|
|
||||||
|
## Projektübersicht
|
||||||
|
|
||||||
|
Monorepo für 5 statische Websites unter moonweb.org, basierend auf Eleventy (11ty), deployt auf Cloudflare Pages.
|
||||||
|
|
||||||
|
### Websites
|
||||||
|
|
||||||
|
| Site | Domain | Zweck | Status |
|
||||||
|
|------|--------|-------|--------|
|
||||||
|
| hub | hub.moonweb.org | Zentrale Indexseite, verlinkt alles | Fertig |
|
||||||
|
| infra | infra.moonweb.org | Infrastruktur-Übersicht (Proxmox, Synology, Netzwerk) | Übersicht + 3 Detailseiten |
|
||||||
|
| smarthome | smarthome.moonweb.org | Smart Home Projekte und Dashboards | Übersicht + 6 Detailseiten |
|
||||||
|
| code | code.moonweb.org | GitHub-Projekte (aggregiert via .moonweb.yml) | Fertig (10 Repos) |
|
||||||
|
| retro | retro.moonweb.org | Physische Retro-Hardware | Nur Übersicht (WIP) |
|
||||||
|
|
||||||
|
### Externe Sites (nicht im Monorepo)
|
||||||
|
|
||||||
|
- stefankoelle.de — Lebenslauf, Kontakt (extern gehostet)
|
||||||
|
- www.moonweb.org — 2000er Internet-Zeitkapsel
|
||||||
|
- 28k8.moonweb.org — 90er BBS/Scene-Archiv
|
||||||
|
- home.moonweb.org — Authelia-geschütztes Homelab-Dashboard
|
||||||
|
|
||||||
|
## Dateistruktur
|
||||||
|
|
||||||
|
```
|
||||||
|
moonweb-site/
|
||||||
|
├── hub/ # Eleventy-Config + index.njk
|
||||||
|
├── infra/ # Eleventy-Config + index.njk + 3 Subseiten
|
||||||
|
│ ├── backup-strategy/
|
||||||
|
│ ├── monitoring/
|
||||||
|
│ └── dev-workflow/
|
||||||
|
├── smarthome/ # Eleventy-Config + index.njk + 6 Subseiten
|
||||||
|
│ ├── homematic-mqtt/
|
||||||
|
│ ├── tasmota-energy/
|
||||||
|
│ ├── balkonpi/
|
||||||
|
│ ├── airplay-audio/
|
||||||
|
│ ├── octoprint/
|
||||||
|
│ └── tubearchivist/
|
||||||
|
├── code/ # Eleventy-Config + index.njk
|
||||||
|
│ └── _data/repos.json # Vom GitHub-Aggregator generiert
|
||||||
|
├── retro/ # Eleventy-Config + index.njk
|
||||||
|
├── shared/ # Gemeinsame Komponenten
|
||||||
|
│ ├── _includes/
|
||||||
|
│ │ ├── base.njk # Basis-Layout (Header, Site-Switcher, Footer)
|
||||||
|
│ │ └── card-grid.njk # Card-Grid Template
|
||||||
|
│ ├── base.css # Basis-CSS (Layout, Cards, Typography)
|
||||||
|
│ └── theme-*.css # Accent-Farben pro Site
|
||||||
|
├── scripts/
|
||||||
|
│ └── github-aggregator/ # Python-Skript für code.moonweb.org
|
||||||
|
│ ├── aggregate.py
|
||||||
|
│ ├── example.moonweb.yml
|
||||||
|
│ └── README.md
|
||||||
|
├── .github/workflows/
|
||||||
|
│ └── build-deploy.yml # CI/CD: Build + Deploy zu Cloudflare Pages
|
||||||
|
├── DESIGN.md # Initiales Konzept mit Design-Entscheidungen (deutsch)
|
||||||
|
├── SPEC.md # Vollständige Spezifikation (148 Zeilen)
|
||||||
|
├── PLAN.md # Implementierungsplan (65 Zeilen)
|
||||||
|
├── README.md # Projekt-README
|
||||||
|
├── TODO.md # Offene Items + Workflow
|
||||||
|
└── package.json # npm scripts für dev/build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dokumentation
|
||||||
|
|
||||||
|
- `DESIGN.md` — Initiales Konzept mit Design-Entscheidungen (deutsch)
|
||||||
|
- `SPEC.md` — Vollständige Spezifikation (148 Zeilen)
|
||||||
|
- `PLAN.md` — Implementierungsplan (65 Zeilen)
|
||||||
|
- `README.md` — Projekt-README
|
||||||
|
- `TODO.md` — Offene Items + Workflow für weitere Arbeit
|
||||||
|
- `TODO.md` — Offene Items + Workflow für weitere Arbeit
|
||||||
|
|
||||||
|
## Technischer Stack
|
||||||
|
|
||||||
|
- **SSG:** Eleventy (11ty) v3.1.6
|
||||||
|
- **Templates:** Nunjucks (.njk)
|
||||||
|
- **CSS:** Variables-basiert mit Accent-Farben pro Site
|
||||||
|
- **Deploy:** Cloudflare Pages (5 separate Projects)
|
||||||
|
- **CI/CD:** GitHub Actions
|
||||||
|
- **GitHub-Aggregator:** Python (liest .moonweb.yml aus Repos)
|
||||||
|
- **Cloudflare Free-Tier:** Max 5 Pages Projects pro Repository
|
||||||
|
|
||||||
|
## Entwicklung
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run dev:hub # localhost:8081
|
||||||
|
npm run dev:infra # localhost:8082
|
||||||
|
npm run dev:smarthome # localhost:8083
|
||||||
|
npm run dev:code # localhost:8084
|
||||||
|
npm run dev:retro # localhost:8085
|
||||||
|
npm run build # Alle Sites bauen
|
||||||
|
```
|
||||||
|
|
||||||
|
## Design-Prinzipien
|
||||||
|
|
||||||
|
1. **Header konsistent** — Identischer Site-Switcher auf allen Home-Sites
|
||||||
|
2. **Content flexibel** — Detailseiten dürfen eigenes Layout haben
|
||||||
|
3. **Accent-Farben:** hub=#3b6ea5, infra=#99333A, smarthome=#1f8a8a, code=#3E5098, retro=#8a6d3b
|
||||||
|
4. **Englisch** — Alle Sites komplett auf Englisch
|
||||||
|
5. **Keine Analytics** — Keine Tracking-Tools
|
||||||
|
6. **Sensible Daten** — Infra-Content wird manuell redigiert (keine IPs, Keys, Passwörter)
|
||||||
|
|
||||||
|
## Content-Regeln (SPEC.md §5)
|
||||||
|
|
||||||
|
| Site | Detailseiten? | Regel |
|
||||||
|
|------|---------------|-------|
|
||||||
|
| smarthome | Ja | Nur wenn genug Content vorhanden |
|
||||||
|
| code | Nie | Nur Übersicht + GitHub-Links |
|
||||||
|
| infra | Kaum | Bewusst oberflächlich (sensible Daten) |
|
||||||
|
| retro | Ja, minimal | Rudimentär, WIP erlaubt |
|
||||||
|
|
||||||
|
## GitHub-Aggregator
|
||||||
|
|
||||||
|
`scripts/github-aggregator/aggregate.py` liest `.moonweb.yml` aus allen public Repos von `skoelle` und schreibt `code/_data/repos.json`. Manuell ausführen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export GITHUB_TOKEN=ghp_xxx
|
||||||
|
python scripts/github-aggregator/aggregate.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## CI/CD
|
||||||
|
|
||||||
|
`.github/workflows/build-deploy.yml` baut alle 5 Sites (hub, infra, smarthome, code, retro) und deployt sie zu Cloudflare Pages.
|
||||||
|
|
||||||
|
Benötigte Secrets:
|
||||||
|
- `CLOUDFLARE_API_TOKEN`
|
||||||
|
- `CLOUDFLARE_ACCOUNT_ID`
|
||||||
|
|
||||||
|
Hinweis: Cloudflare Free-Tier erlaubt nur 5 Projects pro Repository. stefankoelle.de bleibt extern.
|
||||||
|
|
||||||
|
## Offene Punkte
|
||||||
|
|
||||||
|
- retro/ ist bewusst rudimentär gehalten
|
||||||
|
- Einige Cards in smarthome/ und infra/ verlinken noch auf "#" (Placeholder)
|
||||||
|
- Deploy erfolgt noch nicht automatisch (Phase 3 ausstehend)
|
||||||
|
- stefankoelle.de ist extern gehostet, wird nur verlinkt (nicht im Monorepo)
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
# moonweb.org Hub-Konzept (v5 — fast vollständig, letzte Lücken vor SPEC.md/PLAN.md)
|
||||||
|
|
||||||
|
Status: Konzeptrunde nahezu abgeschlossen. Noch keine Umsetzung.
|
||||||
|
|
||||||
|
## 1. Header/Layout-Prinzip — final
|
||||||
|
|
||||||
|
Für alle Home-Bereich-Sites (infra, smarthome, code, retro) gilt ab jetzt ein klares Prinzip:
|
||||||
|
|
||||||
|
- **Header ist das Wichtigste** — identisch über alle vier Sites (Site-Switcher, Banner, Farbakzent der jeweiligen Domain).
|
||||||
|
- **Übersichtsseiten** (Domain-Root) folgen dem einheitlichen Card-Grid-Grundlayout.
|
||||||
|
- **Detailseiten/Subseiten** übernehmen den gleichen Header, dürfen darunter aber ein **eigenes, freieres Layout** haben (wie es bei `/ledmatrix/` auf stefankoelle.de heute schon der Fall ist — Pinbelegung, API-Doku, Fotos in freier Anordnung statt starres Card-Raster).
|
||||||
|
|
||||||
|
Das ist ein wichtiger Unterschied zu v3/v4: nicht "alles identisch", sondern **Header konsistent, Content-Layout pro Subseite flexibel**.
|
||||||
|
|
||||||
|
## 2. LEDMatrix-Migration — verschoben, nicht Teil des Starts
|
||||||
|
|
||||||
|
- Keine Migration von `/ledmatrix/` zum Start. Die Seite bleibt vorerst auf stefankoelle.de.
|
||||||
|
- Aus smarthome.moonweb.org wird lediglich **nach draußen verlinkt** (Übergangslösung).
|
||||||
|
- Migration erfolgt in einer späteren Phase, nicht Teil von Phase 1.
|
||||||
|
- **Keine weiteren Einzelseiten-Migrationen zum Start** — bewusste Entscheidung, jetzt mit der Struktur zu beginnen statt weiter Einzelseiten zu pflegen.
|
||||||
|
|
||||||
|
## 3. Detailtiefe pro Domain — final festgelegt
|
||||||
|
|
||||||
|
| Domain | Detailseiten? | Regel |
|
||||||
|
|---|---|---|
|
||||||
|
| smarthome | Ja, aber nur wenn genug Content da ist | Übersicht zeigt Aufbau des Smarthomes; ein Thema bekommt sofort eine Detailseite, wenn Daten vorhanden sind — sonst wird das Thema vorerst nur in der Übersicht erwähnt (kein Platzhalter-Zwang) |
|
||||||
|
| code | **Nie** | Nur Übersichtskarten + Link zu GitHub — bewusst kein Duplizieren von README-Inhalten |
|
||||||
|
| infra | Kaum, eher übersichtsorientiert | Hauptteil bleibt bewusst oberflächlich, da sensible Daten (IPs, Keys, interne Topologie) nicht öffentlich dürfen — siehe Punkt 4 |
|
||||||
|
| retro | Ja, aber bewusst rudimentär | Thema ist inhaltlich noch unausgereift — nur Grundgerüst anlegen, keine weitere Zeit investieren |
|
||||||
|
|
||||||
|
## 4. Infra-Sensitivität — Redaktionsregel (neu, wichtig für SPEC.md)
|
||||||
|
|
||||||
|
Da infra "eher oberflächlich" bleiben soll, braucht es eine einfache Faustregel, was rein darf und was nicht — sonst ist das bei der Migration der bestehenden Docs (Heimnetzwerk-Final etc.) jedes Mal eine Einzelfallentscheidung:
|
||||||
|
|
||||||
|
- **Darf rein:** Architektur-Ebene (Proxmox + Synology + Docker-Host, VLAN-Konzept ohne konkrete interne IP-Pläne, welche Diensttypen laufen, welche Tools genutzt werden).
|
||||||
|
- **Darf nicht rein:** konkrete IP-Adressen, WireGuard-Keys/Preshared-Keys, Passwörter, interne Hostnamen mit Rückschlussmöglichkeit, Backup-Ziele mit Zugangsdaten.
|
||||||
|
|
||||||
|
Diese Regel gilt 1:1 auch für retro/smarthome, ist aber bei infra am relevantesten, weil dort die Rohdokumente (z. B. Heimnetzwerk-Final-v3.2, GL_Flint2_Final_v2) aktuell sehr konkret sind (u. a. reale IPs, WireGuard-Keys) und beim Übertragen aktiv gekürzt werden müssen.
|
||||||
|
|
||||||
|
## 5. Bilder/Assets — final
|
||||||
|
|
||||||
|
Alle Bilder bleiben **im GitHub-Repo** versioniert (kein separates Cloudflare R2/Images). Einfachster Weg, passt zum ohnehin geplanten Monorepo-Ansatz.
|
||||||
|
|
||||||
|
## 6. DNS — bereits bei Cloudflare
|
||||||
|
|
||||||
|
Zone liegt schon bei Cloudflare, Pages-Anbindung ist damit ohne Zusatzschritt möglich. Offen bleibt nur, wohin die nackte Domain `moonweb.org` zeigt (siehe offene Frage 1 unten).
|
||||||
|
|
||||||
|
## 7. Übersetzung, Last-Updated, Analytics, Perplexity-Rückkanal — final
|
||||||
|
|
||||||
|
- **Übersetzung:** einmaliger KI-gestützter Durchlauf pro Dokument bei der Migration, kein wiederkehrender Prozess.
|
||||||
|
- **Last-Updated-Anzeige:** einfachste Lösung — im Zweifel **gar keine Anzeige**. Stattdessen soll der Anspruch sein, dass Inhalte grundsätzlich aktuell gehalten werden, statt ein Datum zu zeigen, das dann veraltet wirkt.
|
||||||
|
- **Analytics:** keine Statistik-Erfassung, weder Cloudflare Web Analytics noch anderes.
|
||||||
|
- **Perplexity-Rückkanal:** zurückgestellt, wird später erneut betrachtet, keine Zeit jetzt investieren.
|
||||||
|
|
||||||
|
## 8. Offene Kleinigkeiten — beantwortet
|
||||||
|
|
||||||
|
Die vier ursprünglich offenen Punkte wurden inzwischen in SPEC.md/PLAN.md beantwortet:
|
||||||
|
|
||||||
|
1. **Apex-Domain-Routing:** Bleibt bei `www.moonweb.org` Redirect (PLAN.md §3, Phase 4)
|
||||||
|
2. **URL-Konvention:** `domain/slug/`, kleinschreibung, Bindestriche (SPEC.md §4.3)
|
||||||
|
3. **Start-Reihenfolge:** Alle Sites gleichzeitig (PLAN.md §1: "launch simultaneously")
|
||||||
|
4. **Platzhalter-Verhalten:** Keine Platzhalter, ehrliche kurze Seiten + WIP-Hinweise (SPEC.md §5, retro/ hat WIP-Badge)
|
||||||
|
|
||||||
|
## 9. Gesamtstand — Konzept vollständig
|
||||||
|
|
||||||
|
Alle Entscheidungsgrundlagen sind in SPEC.md (was) und PLAN.md (wie/wann) umgesetzt:
|
||||||
|
|
||||||
|
- Sitemap, Domain-Zwecke, Namensgebung
|
||||||
|
- Header-konsistent/Content-flexibel-Prinzip
|
||||||
|
- Detailtiefe pro Domain
|
||||||
|
- Redaktionsregel für sensible infra-Inhalte
|
||||||
|
- Monorepo-Struktur, Eleventy als SSG, GitHub Actions Build, Cloudflare Pages Deploy über 6 Projekte
|
||||||
|
- Sprache komplett Englisch
|
||||||
|
- Bilder im Repo, DNS bei Cloudflare, Übersetzung einmalig, keine Last-Updated-Anzeige, keine Analytics, Perplexity-Rückkanal zurückgestellt
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
# moonweb.org — Implementation Plan
|
||||||
|
|
||||||
|
Companion to `SPEC.md`. Defines order of work. All five sites (hub, infra, smarthome, code, retro) launch **simultaneously** — no staged rollout by domain. cv (stefankoelle.de) stays external (Cloudflare Free-Tier limits to 5 Pages projects per repository).
|
||||||
|
|
||||||
|
## Phase 0 — Repo & tooling setup
|
||||||
|
|
||||||
|
1. Create the `moonweb-site` monorepo (private until first launch, then can stay private since Pages deploys the built output, not the source).
|
||||||
|
2. Initialize Eleventy project structure per `SPEC.md §10`.
|
||||||
|
3. Set up `shared/layout-base.njk` (header + card-grid) and one `theme-*.css` per accent color (hub, infra, smarthome, code, retro).
|
||||||
|
4. Verify local dev server works per site (`npm run dev:<site>`) before any content work starts.
|
||||||
|
|
||||||
|
## Phase 1 — Content migration & authoring (per domain)
|
||||||
|
|
||||||
|
Work order within this phase is flexible since all domains launch together; suggested sequence based on how self-contained each domain's content is:
|
||||||
|
|
||||||
|
1. **code** — simplest case, no detail pages, no sensitive-data filtering needed.
|
||||||
|
- Add `.moonweb.yml` to each GitHub repo (see `SPEC.md §8`).
|
||||||
|
- Build the aggregator script, run it once, generate `code/_data/repos.json`.
|
||||||
|
- Build the overview page grouped by subcategory (Automation & Sync, Dev-Tools, Web Apps, Firmware/Hardware, Misc).
|
||||||
|
2. **smarthome** — overview first, detail pages only where content already exists.
|
||||||
|
- Draft the overview: how the smart home is structured (mirrors what's on `home.moonweb.org`: buttons/control, sensors, calendar, weather, media).
|
||||||
|
- For each existing topic with enough material (HomematicIP/MQTT, Tasmota, LED-Matrix *reference only, no migration*, M5Stack/WT32SC01 dashboards, WetterAPI, AirPlay, OctoPi, TubeArchivist), decide case by case: enough content → detail page; too thin → mention in overview only, no placeholder.
|
||||||
|
- Translate source material to English during authoring (one-time AI pass per document, per `SPEC.md §7`).
|
||||||
|
3. **infra** — overview only, redaction pass required.
|
||||||
|
- Draft a shallow, structured overview: Proxmox/pve2, Synology, VLANs, Fritz!Box mesh, SSO, Docker hosting model, plus the "how I work" section (OpenClaw/OpenCode setup, dev workflow).
|
||||||
|
- Apply the redaction rule from `SPEC.md §6` while translating — strip IPs, keys, credentials, internal hostnames from every source document before it becomes public content.
|
||||||
|
- Skip detail pages unless a topic can be described without any sensitive detail.
|
||||||
|
4. **retro** — minimal effort, rudimentary only.
|
||||||
|
- One overview page listing what hardware exists.
|
||||||
|
- Add "work in progress" notices for anything that isn't ready — better an honest short page than none.
|
||||||
|
5. **hub** — build last within this phase since it links to all the others.
|
||||||
|
- One-line description + link per destination (infra, smarthome, code, retro, and external links to stefankoelle.de, www.moonweb.org, 28k8.moonweb.org, home.moonweb.org).
|
||||||
|
|
||||||
|
## Phase 2 — CI/CD
|
||||||
|
|
||||||
|
1. Write `.github/workflows/build-deploy.yml`: builds all five sites in one job (or matrix), then deploys each output folder to its corresponding Cloudflare Pages project via `wrangler pages deploy`.
|
||||||
|
2. Create five Cloudflare Pages projects (hub, infra, smarthome, code, retro), each bound to its target custom domain (DNS already on Cloudflare, no extra setup needed). Note: Cloudflare Free-Tier limits to 5 Pages projects per repository — cv (stefankoelle.de) stays external.
|
||||||
|
3. Do one full dry run per site locally before the first real deploy.
|
||||||
|
|
||||||
|
## Phase 3 — Launch
|
||||||
|
|
||||||
|
1. Deploy all five sites simultaneously.
|
||||||
|
2. Point the respective custom domains at their Pages projects.
|
||||||
|
3. Apex domain `moonweb.org` keeps redirecting to `www.moonweb.org` for now (no change in this launch).
|
||||||
|
4. Smoke-test cross-linking: hub → each site, site-switcher on infra/smarthome/code/retro, smarthome → external ledmatrix link, code cards → GitHub links.
|
||||||
|
|
||||||
|
## Phase 4 — Explicitly deferred (not part of this build)
|
||||||
|
|
||||||
|
- Migrating `/ledmatrix/` and any other stefankoelle.de sub-pages into smarthome.
|
||||||
|
- Deciding on 28k8.moonweb.org's future (stay separate vs. eventual monorepo inclusion).
|
||||||
|
- Building any Perplexity-backchannel mechanism for reusing published site content in this project.
|
||||||
|
- Redirecting the apex domain from `www.moonweb.org` to `hub.moonweb.org`.
|
||||||
|
- Any analytics, "last updated" timestamps, or scheduled/automated content generation beyond the one-time GitHub aggregator run.
|
||||||
|
- Porting stefankoelle.de into the monorepo — stays external (Cloudflare Free-Tier limits to 5 Pages projects).
|
||||||
|
|
||||||
|
## Definition of done for this build
|
||||||
|
|
||||||
|
- All five sites live on Cloudflare Pages under their intended domains.
|
||||||
|
- code.moonweb.org reflects the current GitHub repos via the `.moonweb.yml` aggregator, grouped by subcategory.
|
||||||
|
- smarthome.moonweb.org gives an accurate picture of what's running on home.moonweb.org today, with detail pages only where content already existed.
|
||||||
|
- infra.moonweb.org describes the stack shallowly with zero sensitive data leaked.
|
||||||
|
- retro.moonweb.org exists with an honest, minimal overview (WIP notices allowed).
|
||||||
|
- hub.moonweb.org correctly links everything, including the untouched external sites (stefankoelle.de, www.moonweb.org, 28k8.moonweb.org).
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# moonweb-site
|
||||||
|
|
||||||
|
Monorepo for hub.moonweb.org, infra.moonweb.org, smarthome.moonweb.org,
|
||||||
|
code.moonweb.org and retro.moonweb.org. cv (stefankoelle.de) stays external
|
||||||
|
(Cloudflare Free-Tier limits to 5 Pages projects per repository).
|
||||||
|
|
||||||
|
See `SPEC.md` for the full specification and `PLAN.md` for the phased
|
||||||
|
implementation plan. Cloudflare Free-Tier limits to 5 Pages projects per
|
||||||
|
repository — cv (stefankoelle.de) stays external.
|
||||||
|
|
||||||
|
## What is real vs. placeholder
|
||||||
|
|
||||||
|
- Structure, layouts, themes, Eleventy configs, CI workflow: fully implemented.
|
||||||
|
- `code/_data/repos.json`: populated with real data pulled from the
|
||||||
|
`skoelle` GitHub account (public repos only, private repos like
|
||||||
|
`wetterapi` intentionally excluded from the public catalog).
|
||||||
|
- `infra/`, `smarthome/`: overview pages and detail pages with real content,
|
||||||
|
redacted per SPEC.md §6.
|
||||||
|
- `retro/`: intentionally minimal, per SPEC.md §5.
|
||||||
|
|
||||||
|
Note: stefankoelle.de (CV) is hosted separately and linked from the hub
|
||||||
|
and site-switcher — it is not part of this monorepo (Cloudflare Free-Tier
|
||||||
|
limits to 5 Pages projects per repository).
|
||||||
|
|
||||||
|
## Local development
|
||||||
|
|
||||||
|
Each site has its own minimal Eleventy config so you can preview one site
|
||||||
|
at a time:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run dev:hub # http://localhost:8081
|
||||||
|
npm run dev:infra # http://localhost:8082
|
||||||
|
npm run dev:smarthome # http://localhost:8083
|
||||||
|
npm run dev:code # http://localhost:8084
|
||||||
|
npm run dev:retro # http://localhost:8085
|
||||||
|
```
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build # builds all five Eleventy sites into dist/<site>/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deploy
|
||||||
|
|
||||||
|
Handled entirely by `.github/workflows/build-deploy.yml` on push to `main`:
|
||||||
|
builds all sites, then deploys each `dist/<site>` folder to its own
|
||||||
|
Cloudflare Pages project via `wrangler pages deploy`. See the workflow
|
||||||
|
file for the required repo secrets.
|
||||||
|
|
||||||
|
Note: Cloudflare Free-Tier limits to 5 Pages projects per repository —
|
||||||
|
cv (stefankoelle.de) stays external.
|
||||||
|
|
||||||
|
## GitHub aggregator (code.moonweb.org)
|
||||||
|
|
||||||
|
`scripts/github-aggregator/aggregate.py` reads a `.moonweb.yml` file from
|
||||||
|
each of your GitHub repos and regenerates `code/_data/repos.json`. Run it
|
||||||
|
manually whenever you want to refresh the catalog — see SPEC.md §8 for the
|
||||||
|
`.moonweb.yml` schema. It is *not* wired into CI; this is a deliberate,
|
||||||
|
manual step. cv (stefankoelle.de) is not included in this automation.
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
# moonweb.org — Specification
|
||||||
|
|
||||||
|
Status: Concept finalized. This document defines *what* gets built. See `PLAN.md` for *how and in what order*.
|
||||||
|
|
||||||
|
## 1. Purpose
|
||||||
|
|
||||||
|
A personal homelab hub consisting of a central index (`hub`) and several themed static sites, replacing the current unstructured presentation of infrastructure, smart home projects, and GitHub repos. cv (stefankoelle.de) remains external, linked from the hub and site-switcher.
|
||||||
|
|
||||||
|
## 2. Sitemap
|
||||||
|
|
||||||
|
```
|
||||||
|
moonweb-site (monorepo)
|
||||||
|
├── hub/ → hub.moonweb.org Central index & gateway
|
||||||
|
├── infra/ → infra.moonweb.org System architecture / stack overview
|
||||||
|
├── smarthome/ → smarthome.moonweb.org What the homelab actually runs, and why
|
||||||
|
├── code/ → code.moonweb.org Curated GitHub catalog
|
||||||
|
└── retro/ → retro.moonweb.org Physical retro hardware collection
|
||||||
|
|
||||||
|
outside the monorepo, untouched:
|
||||||
|
├── stefankoelle.de CV, career, personal (extern gehostet)
|
||||||
|
├── www.moonweb.org finished, no overlap (2001-style internet time capsule)
|
||||||
|
├── 28k8.moonweb.org 90s BBS/scene archive, separate approach, may migrate later (undecided)
|
||||||
|
└── home.moonweb.org Authelia-protected homelab dashboard/control plane (not part of this site set)
|
||||||
|
```
|
||||||
|
|
||||||
|
Apex domain `moonweb.org` currently redirects to `www.moonweb.org`. This stays as-is for now; a later redirect to `hub.moonweb.org` is possible but out of scope for this build. Cloudflare Free-Tier limits to 5 Pages projects per repository, which is why cv (stefankoelle.de) stays external.
|
||||||
|
|
||||||
|
## 3. Domain purposes
|
||||||
|
|
||||||
|
| Domain | Purpose | Tone |
|
||||||
|
|---|---|---|
|
||||||
|
| hub | Gateway, links to everything, one-line description per destination | Minimal |
|
||||||
|
| infra | Shallow, structured overview of the stack: Proxmox (PVE + pve2), Synology DS918+, VLANs, Fritz!Box mesh, SSO, plus how I work (OpenCode/OpenClaw dev environment) | Reference, high-level only |
|
||||||
|
| smarthome | Why the homelab exists — what's actually running on `home.moonweb.org` as smart home: sensors, automation, calendar/contacts sync, dashboards, media | Project storytelling |
|
||||||
|
| code | Curated, sorted GitHub catalog — overview only, always linking out to GitHub | Portfolio |
|
||||||
|
| retro | Physical retro hardware collection (not software/demos — that's 28k8's domain) | Simple, factual |
|
||||||
|
|
||||||
|
## 4. Design system
|
||||||
|
|
||||||
|
### 4.1 Header-consistent, content-flexible principle
|
||||||
|
|
||||||
|
- **Header is identical** across infra/smarthome/code/retro: site-switcher (hub · infra · smarthome · code · retro), domain accent color, consistent branding.
|
||||||
|
- **Overview (index) pages** use a shared card-grid layout (reference: current `home.moonweb.org` — banner header, grouped card sections, clean sans-serif, generous whitespace, light theme only, no heavy JS).
|
||||||
|
- **Detail/sub-pages** keep the same header but may use a freer layout below it (reference: `/ledmatrix/` on stefankoelle.de today — pin tables, API docs, photos in free layout instead of a rigid card grid).
|
||||||
|
|
||||||
|
Note: cv (stefankoelle.de) is external and uses its own independent design — it does not follow this principle.
|
||||||
|
|
||||||
|
### 4.2 Accent colors per domain
|
||||||
|
|
||||||
|
| Domain | Accent |
|
||||||
|
|---|---|
|
||||||
|
| hub | Neutral blue |
|
||||||
|
| infra | Grey-blue |
|
||||||
|
| smarthome | Teal |
|
||||||
|
| code | Violet |
|
||||||
|
| retro | Own accent, still clean card-grid (no 90s styling — that belongs to 28k8) |
|
||||||
|
|
||||||
|
### 4.3 URL convention
|
||||||
|
|
||||||
|
`domain/slug/` — lowercase, hyphenated, trailing slash, matching the existing stefankoelle.de pattern (e.g. `/ledmatrix/`) so future detail pages stay consistent even if content later migrates between sites. cv (stefankoelle.de) follows its own existing URL patterns and is not part of this convention.
|
||||||
|
|
||||||
|
## 5. Content depth rules (per domain)
|
||||||
|
|
||||||
|
| Domain | Detail pages? | Rule |
|
||||||
|
|---|---|---|
|
||||||
|
| smarthome | Yes, when there's enough content | Index shows how the smart home is structured; a topic gets a detail page immediately if enough data exists — otherwise it's mentioned in the overview only, no placeholder required |
|
||||||
|
| code | Never | Overview cards + link to GitHub only. No duplicating README content. |
|
||||||
|
| infra | Rarely | Deliberately shallow — most of the raw material is sensitive (see §6) |
|
||||||
|
| retro | Yes, but minimal | Topic is still immature; create only a rudimentary overview, don't over-invest time |
|
||||||
|
|
||||||
|
General rule across all domains: **if content is too thin for a good detail page, skip the detail page — don't create a placeholder.**
|
||||||
|
|
||||||
|
Note: cv (stefankoelle.de) is external and not subject to these rules.
|
||||||
|
|
||||||
|
## 6. Infra content redaction rule
|
||||||
|
|
||||||
|
Because infra must stay shallow and public-safe:
|
||||||
|
|
||||||
|
- **Allowed:** architecture level — Proxmox + Synology + Docker host, VLAN concept without concrete internal IP plans, which service types run, which tools are used.
|
||||||
|
- **Not allowed:** concrete IP addresses, WireGuard keys/preshared keys, passwords, internal hostnames that allow inference, backup targets with credentials.
|
||||||
|
|
||||||
|
This rule applies to any domain but is most relevant for infra, since the source documents (e.g. Heimnetzwerk-Final-v3.2, GL_Flint2_Final_v2) currently contain real IPs and keys that must be actively stripped during migration. cv (stefankoelle.de) is maintained separately and not subject to this rule.
|
||||||
|
|
||||||
|
## 7. Language
|
||||||
|
|
||||||
|
All five sites (hub, infra, smarthome, code, retro) are written **entirely in English**. Existing German source documents are translated once during migration via a single AI-assisted pass — not a recurring process. New `.moonweb.yml` metadata and generated content are authored in English from the start. cv (stefankoelle.de) remains in its existing language.
|
||||||
|
|
||||||
|
## 8. GitHub automation (code.moonweb.org)
|
||||||
|
|
||||||
|
Each GitHub project repo gets a `.moonweb.yml` in its root:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
title: "MVG Departures"
|
||||||
|
category: code # code | smarthome | infra
|
||||||
|
subcategory: "Web Apps" # drives grouping on code.moonweb.org
|
||||||
|
status: active
|
||||||
|
stack: [Python, FastAPI]
|
||||||
|
hosted_on: "Docker Host Debian (PVE)"
|
||||||
|
summary: "Compact MVG/S-Bahn departure monitor with configurable stations."
|
||||||
|
repo_url: "https://github.com/skoelle/mvg-departures"
|
||||||
|
```
|
||||||
|
|
||||||
|
A local aggregator script reads `.moonweb.yml` from all repos via the GitHub API, an AI pass turns the raw YAML into readable card copy, and the result is committed into `code/_data/repos.json` inside the monorepo. This runs manually, on demand — no scheduled automation for now. cv (stefankoelle.de) is not included in this automation.
|
||||||
|
|
||||||
|
## 9. Content maintenance
|
||||||
|
|
||||||
|
- infra, smarthome, retro: **fully manual**, edited in vim, committed via git push. No automation.
|
||||||
|
- code: the only automated piece is the GitHub aggregator described in §8.
|
||||||
|
- No "last updated" timestamps are shown anywhere — the goal is that content is simply kept current, not that staleness is displayed.
|
||||||
|
- No analytics/tracking of any kind on any of the five sites.
|
||||||
|
- Images/assets live versioned directly in the monorepo (no external asset host).
|
||||||
|
- cv (stefankoelle.de): maintained separately, linked from hub/site-switcher.
|
||||||
|
|
||||||
|
## 10. Repository structure
|
||||||
|
|
||||||
|
```
|
||||||
|
moonweb-site/
|
||||||
|
├── hub/
|
||||||
|
├── infra/
|
||||||
|
├── smarthome/
|
||||||
|
├── code/
|
||||||
|
│ └── _data/repos.json # populated by the GitHub aggregator
|
||||||
|
├── retro/
|
||||||
|
├── shared/
|
||||||
|
│ ├── _includes/
|
||||||
|
│ │ ├── base.njk # shared header + footer layout
|
||||||
|
│ │ └── card-grid.njk # card-grid template
|
||||||
|
│ ├── base.css # shared CSS variables and layout
|
||||||
|
│ └── theme-*.css # one accent color file per domain
|
||||||
|
├── scripts/
|
||||||
|
│ └── github-aggregator/ # reads .moonweb.yml from all repos
|
||||||
|
├── DESIGN.md # initial concept and design decisions
|
||||||
|
├── SPEC.md # what gets built (this document)
|
||||||
|
├── PLAN.md # how and in what order
|
||||||
|
├── TODO.md # open items and workflow
|
||||||
|
└── .github/workflows/
|
||||||
|
└── build-deploy.yml # builds all sites, deploys each to its Cloudflare Pages project
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: `cv/` (stefankoelle.de) is hosted externally and linked from hub/site-switcher — it is not part of this monorepo (Cloudflare Free-Tier limits to 5 Pages projects per repository).
|
||||||
|
|
||||||
|
## 11. Technical stack
|
||||||
|
|
||||||
|
- **Static site generator:** Eleventy (11ty) — markdown/YAML-first, minimal JS, `_data` folders map directly onto the `.moonweb.yml` aggregator output, low maintenance for five sites at this scale.
|
||||||
|
- **Build:** entirely in GitHub Actions.
|
||||||
|
- **Deploy:** Cloudflare Pages — five separate Pages projects (one per public domain: hub, infra, smarthome, code, retro), since Cloudflare Pages binds one custom-domain set per project. One shared GitHub Actions workflow builds all five sites and deploys each output folder to its respective Pages project.
|
||||||
|
- **Cloudflare Free-Tier limit:** Max 5 Pages projects per repository — cv (stefankoelle.de) stays external to avoid hitting this limit.
|
||||||
|
- **Runtime:** fully static, no server-side code, no containers for the website itself (distinct from the actual homelab services running on the Docker host).
|
||||||
|
- **DNS:** already on Cloudflare — no additional setup step needed for Pages custom domains.
|
||||||
|
- **Local preview:** Eleventy's built-in dev server with live reload, run per-site (`npm run dev:<site>`) before any commit.
|
||||||
|
|
||||||
|
## 12. Explicitly out of scope for this build
|
||||||
|
|
||||||
|
- Migrating `/ledmatrix/` (and any other existing stefankoelle.de sub-pages) into smarthome — stays linked externally for now, migrates in a later phase.
|
||||||
|
- Migrating 28k8.moonweb.org into the monorepo — undecided, revisit later, likely never.
|
||||||
|
- Any Perplexity-backchannel mechanism (website content reusable inside this project) — deferred, no time invested now.
|
||||||
|
- Analytics of any kind.
|
||||||
|
- Automated content generation/translation pipelines beyond the one-time GitHub aggregator for code and the one-time translation pass during migration.
|
||||||
|
- Porting stefankoelle.de into the monorepo — stays external (Cloudflare Free-Tier limits to 5 Pages projects per repository).
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# TODO — moonweb-site Offene Items
|
||||||
|
|
||||||
|
Stand: 2026-08-13
|
||||||
|
|
||||||
|
## Aktueller Status
|
||||||
|
|
||||||
|
| Phase | Status | Bemerkung |
|
||||||
|
|-------|--------|-----------|
|
||||||
|
| Phase 0 — Repo & Tooling | ✅ fertig | Eleventy 3.1.6, CI/CD, .gitignore erweitert |
|
||||||
|
| Phase 1 — Content | 🟡 80% | hub, code, infra, smarthome fast fertig, retro offen |
|
||||||
|
| Phase 2 — CI/CD | ✅ fertig | build-deploy.yml mit Caching + Validation + Summary |
|
||||||
|
| Phase 3 — Launch | 🟡 fast ready | Impressum + Favicons + Meta-Tags + CSS fertig |
|
||||||
|
| Phase 4 — Deferred | ⏸️ zurückgestellt | Ledmatrix-Migration, 28k8, Analytics |
|
||||||
|
|
||||||
|
## Offene Items — nach Priorität
|
||||||
|
|
||||||
|
### P1 — Muss vor Launch
|
||||||
|
|
||||||
|
- [ ] **Placeholder-Cards aufräumen** — 12 Cards verlinken noch auf "#"
|
||||||
|
- infra/ (7x): PVE, pve2, Docker-VM, Synology, Fritz!Box, OpenWRT, IoT VLAN
|
||||||
|
- smarthome/ (3x): Dashboard Controls, Air Quality, Weather
|
||||||
|
- retro/ (3x): alle Cards
|
||||||
|
- Entweder echte Ziel-URLs, Karte entfernen, oder als nicht-klickbare Info-Items darstellen
|
||||||
|
|
||||||
|
- [ ] **Content-Review** — Vor Launch einmal alle Seiten durchgehen
|
||||||
|
|
||||||
|
### P2 — Für Launch
|
||||||
|
|
||||||
|
- [ ] **Cloudflare Pages Projects erstellen**
|
||||||
|
- moonweb-hub, moonweb-infra, moonweb-smarthome, moonweb-code, moonweb-retro
|
||||||
|
- Hinweis: Cloudflare Free-Tier erlaubt nur 5 Projects pro Repository
|
||||||
|
|
||||||
|
- [ ] **DNS & Custom Domains binden**
|
||||||
|
- hub.moonweb.org, infra.moonweb.org, smarthome.moonweb.org, code.moonweb.org, retro.moonweb.org
|
||||||
|
|
||||||
|
- [ ] **Secrets in GitHub Repo prüfen**
|
||||||
|
- CLOUDFLARE_API_TOKEN
|
||||||
|
- CLOUDFLARE_ACCOUNT_ID
|
||||||
|
|
||||||
|
### P3 — Nach Launch
|
||||||
|
|
||||||
|
- [ ] Smoke-Test Cross-Linking (hub → alle Sites, Site-Switcher, externe Links)
|
||||||
|
- [ ] README.md aktualisieren (aktuellen Stand beschreiben)
|
||||||
|
|
||||||
|
### P4 — Deferred (nicht dringend)
|
||||||
|
|
||||||
|
- [ ] Ledmatrix-Migration nach smarthome
|
||||||
|
- [ ] 28k8.moonweb.org Zukunft klären
|
||||||
|
- [ ] Perplexity-Rückkanal
|
||||||
|
- [ ] Apex-Domain Redirect (moonweb.org → hub.moonweb.org)
|
||||||
|
- [ ] stefankoelle.de in Monorepo portieren (nur wenn Cloudflare Free-Tier-Limit gelöst wird)
|
||||||
|
|
||||||
|
## Schon erledigt (Stand 2026-08-13)
|
||||||
|
|
||||||
|
- [x] Impressum angelegt + Footer-Link auf allen Sites
|
||||||
|
- [x] Favicons (SVG mit Emojis) pro Subdomain
|
||||||
|
- [x] Meta descriptions auf allen 14 Seiten
|
||||||
|
- [x] CSS @import-Kette bereinigt (kein Render-Blocking mehr)
|
||||||
|
- [x] .gitignore erweitert (.env*, Zertifikate, Swap-Files)
|
||||||
|
|
||||||
|
## Workflow für neue Contexts
|
||||||
|
|
||||||
|
Wenn du einen neuen Context öffnest, lies diese Datei und prüfe:
|
||||||
|
1. Welche P1-Items sind noch offen? → Davor arbeiten
|
||||||
|
2. Welche P2-Items blockieren den Launch? → Parallel klären
|
||||||
|
3. P3/P4 nur wenn angefordert
|
||||||
|
|
||||||
|
## Definition of done
|
||||||
|
|
||||||
|
Alle 5 Sites live auf Cloudflare Pages:
|
||||||
|
- ✅ hub.moonweb.org — verlinkt alles (+ stefankoelle.de extern)
|
||||||
|
- ✅ code.moonweb.org — GitHub-Katalog via Aggregator
|
||||||
|
- ✅ smarthome.moonweb.org — Smart Home Übersicht + Detailseiten
|
||||||
|
- ✅ infra.moonweb.org — Infra-Übersicht (redigiert)
|
||||||
|
- ✅ retro.moonweb.org — Ehrliche minimale Übersicht (WIP)
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"title": "Calendar Sync",
|
||||||
|
"repo": "calender_sync",
|
||||||
|
"subcategory": "Automation & Sync",
|
||||||
|
"status": "active",
|
||||||
|
"stack": [
|
||||||
|
"Python",
|
||||||
|
"FastAPI",
|
||||||
|
"MariaDB"
|
||||||
|
],
|
||||||
|
"summary": "Syncs a private Google Calendar (ICS) into MariaDB, with recurring-event expansion, soft-delete tracking, and a FastAPI web UI/REST API.",
|
||||||
|
"repo_url": "https://github.com/skoelle/calender_sync"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "iCloud Contacts Sync",
|
||||||
|
"repo": "icloud-contacts-sync",
|
||||||
|
"subcategory": "Automation & Sync",
|
||||||
|
"status": "active",
|
||||||
|
"stack": [
|
||||||
|
"Python",
|
||||||
|
"MariaDB",
|
||||||
|
"CardDAV"
|
||||||
|
],
|
||||||
|
"summary": "Automated iCloud contacts sync to MariaDB via CardDAV delta sync, multi-user account mapping, birthday email digest, and a read-only API/web view behind Authelia.",
|
||||||
|
"repo_url": "https://github.com/skoelle/icloud-contacts-sync"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "DynDNS Updater",
|
||||||
|
"repo": "dyndns-updater",
|
||||||
|
"subcategory": "Automation & Sync",
|
||||||
|
"status": "active",
|
||||||
|
"stack": [
|
||||||
|
"Python",
|
||||||
|
"Docker"
|
||||||
|
],
|
||||||
|
"summary": "Cloudflare + FreeDNS DynDNS updater for FritzBox (TR-064), packaged as a Docker container.",
|
||||||
|
"repo_url": "https://github.com/skoelle/dyndns-updater"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "kctl-tui",
|
||||||
|
"repo": "kctl-tui",
|
||||||
|
"subcategory": "Dev Tools",
|
||||||
|
"status": "active",
|
||||||
|
"stack": [
|
||||||
|
"Go",
|
||||||
|
"Bubble Tea"
|
||||||
|
],
|
||||||
|
"summary": "A terminal entry point for everyday Kubernetes work: guided context/team/namespace selection, a 3-pane k9s-style status view, guided rollout restarts, and an AWS Secrets Manager <-> Kubernetes Secret diff/sync workflow.",
|
||||||
|
"repo_url": "https://github.com/skoelle/kctl-tui"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "FocusApp",
|
||||||
|
"repo": "focusapp",
|
||||||
|
"subcategory": "Web Apps",
|
||||||
|
"status": "active",
|
||||||
|
"stack": [
|
||||||
|
"React",
|
||||||
|
"TypeScript",
|
||||||
|
"ASP.NET Core",
|
||||||
|
"MariaDB"
|
||||||
|
],
|
||||||
|
"summary": "A clean, modern todo application with drag & drop reordering. Responsive design, MariaDB persistence, Docker deployment.",
|
||||||
|
"repo_url": "https://github.com/skoelle/focusapp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "PeopleCostCounter",
|
||||||
|
"repo": "PeopleCostCounter",
|
||||||
|
"subcategory": "Web Apps",
|
||||||
|
"status": "active",
|
||||||
|
"stack": [
|
||||||
|
"Vanilla JS"
|
||||||
|
],
|
||||||
|
"summary": "A single-page tool that shows in real time how much a meeting is costing, based on team size and average salary, with English and German UIs.",
|
||||||
|
"repo_url": "https://github.com/skoelle/PeopleCostCounter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "MVG Departures",
|
||||||
|
"repo": "mvg-departures",
|
||||||
|
"subcategory": "Web Apps",
|
||||||
|
"status": "active",
|
||||||
|
"stack": [
|
||||||
|
"Python",
|
||||||
|
"FastAPI"
|
||||||
|
],
|
||||||
|
"summary": "Compact MVG/S-Bahn departure monitor with configurable stations, direction filters, and a mobile-friendly HTML/JSON view.",
|
||||||
|
"repo_url": "https://github.com/skoelle/mvg-departures"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "M5Stack Dashboard",
|
||||||
|
"repo": "m5stack-dashboard",
|
||||||
|
"subcategory": "Firmware & Hardware",
|
||||||
|
"status": "active",
|
||||||
|
"stack": [
|
||||||
|
"C++",
|
||||||
|
"PlatformIO",
|
||||||
|
"ESP32"
|
||||||
|
],
|
||||||
|
"summary": "M5Stack Core dashboard showing live weather, calendar events, and MVG departures. Navigate with 3 physical buttons, dark iOS-style UI.",
|
||||||
|
"repo_url": "https://github.com/skoelle/m5stack-dashboard"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "WT32-SC01 Dashboard",
|
||||||
|
"repo": "wt32sc01-dashboard",
|
||||||
|
"subcategory": "Firmware & Hardware",
|
||||||
|
"status": "active",
|
||||||
|
"stack": [
|
||||||
|
"C++",
|
||||||
|
"PlatformIO",
|
||||||
|
"LovyanGFX",
|
||||||
|
"LVGL"
|
||||||
|
],
|
||||||
|
"summary": "Touch dashboard for the WT32-SC01 Plus (ESP32-S3, compact display) showing weather, calendar events and MVG departures from local APIs.",
|
||||||
|
"repo_url": "https://github.com/skoelle/wt32sc01-dashboard"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Marcer GameDVD Launcher",
|
||||||
|
"repo": "marcer-gamedvd-launcher",
|
||||||
|
"subcategory": "Misc",
|
||||||
|
"status": "active",
|
||||||
|
"stack": [
|
||||||
|
"C#"
|
||||||
|
],
|
||||||
|
"summary": "Console launcher for a Atari ST/Falcon GameDVD USB stick — browse, compare, and launch games from the terminal with overlay/patch mode.",
|
||||||
|
"repo_url": "https://github.com/skoelle/marcer-gamedvd-launcher"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
module.exports = function (eleventyConfig) {
|
||||||
|
eleventyConfig.addPassthroughCopy({ "shared/theme-code.css": "theme.css" });
|
||||||
|
eleventyConfig.addPassthroughCopy({ "shared/base.css": "shared-base.css" });
|
||||||
|
eleventyConfig.addPassthroughCopy({ "shared/favicon/code.svg": "favicon.svg" });
|
||||||
|
|
||||||
|
return {
|
||||||
|
dir: {
|
||||||
|
input: "code",
|
||||||
|
includes: "../shared/_includes",
|
||||||
|
output: "dist/code"
|
||||||
|
},
|
||||||
|
serverOptions: {
|
||||||
|
host: "0.0.0.0",
|
||||||
|
port: 8084
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
title: "Code"
|
||||||
|
section: "code"
|
||||||
|
description: "Curated catalog of my public GitHub projects — automation, dev tools, web apps, and firmware."
|
||||||
|
layout: base.njk
|
||||||
|
subcategories:
|
||||||
|
- "Automation & Sync"
|
||||||
|
- "Dev Tools"
|
||||||
|
- "Web Apps"
|
||||||
|
- "Firmware & Hardware"
|
||||||
|
- "Misc"
|
||||||
|
---
|
||||||
|
<p>A curated, sorted catalog of my public GitHub projects. Each card links
|
||||||
|
directly to its repository.</p>
|
||||||
|
|
||||||
|
{% for subcat in subcategories %}
|
||||||
|
<section class="card-section">
|
||||||
|
<h2>{{ subcat }}</h2>
|
||||||
|
<div class="card-grid">
|
||||||
|
{% for repo in repos %}
|
||||||
|
{% if repo.subcategory == subcat %}
|
||||||
|
<a class="card" href="{{ repo.repo_url }}">
|
||||||
|
<h3>{{ repo.title }}</h3>
|
||||||
|
<p>{{ repo.summary }}</p>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endfor %}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
module.exports = function (eleventyConfig) {
|
||||||
|
eleventyConfig.addPassthroughCopy({ "shared/theme-hub.css": "theme.css" });
|
||||||
|
eleventyConfig.addPassthroughCopy({ "shared/base.css": "shared-base.css" });
|
||||||
|
eleventyConfig.addPassthroughCopy({ "shared/favicon/hub.svg": "favicon.svg" });
|
||||||
|
|
||||||
|
return {
|
||||||
|
dir: {
|
||||||
|
input: "hub",
|
||||||
|
includes: "../shared/_includes",
|
||||||
|
output: "dist/hub"
|
||||||
|
},
|
||||||
|
serverOptions: {
|
||||||
|
host: "0.0.0.0",
|
||||||
|
port: 8081
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
title: "Impressum"
|
||||||
|
section: "hub"
|
||||||
|
description: "Legal imprint (Impressum) for moonweb.org."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
|
||||||
|
<h1>Impressum</h1>
|
||||||
|
|
||||||
|
<address>
|
||||||
|
Stefan Kölle<br>
|
||||||
|
Neumarkter Str. 86c<br>
|
||||||
|
81673 München
|
||||||
|
</address>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Phone/Fax: +49-(89)-20006547<br>
|
||||||
|
E-Mail: <a href="mailto:info@inseco.de">info@inseco.de</a>
|
||||||
|
</p>
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
title: "Home"
|
||||||
|
section: "hub"
|
||||||
|
description: "moonweb.org — my personal homelab: smarthome, infrastructure, code, and retro hardware."
|
||||||
|
layout: base.njk
|
||||||
|
sections:
|
||||||
|
- heading: "Homelab sites"
|
||||||
|
cards:
|
||||||
|
- title: "infra"
|
||||||
|
summary: "Stack overview: Proxmox, Synology, network, backups, and how I work."
|
||||||
|
href: "https://infra.moonweb.org"
|
||||||
|
emoji: "🏗️"
|
||||||
|
- title: "smarthome"
|
||||||
|
summary: "What's actually running on home.moonweb.org, and why."
|
||||||
|
href: "https://smarthome.moonweb.org"
|
||||||
|
emoji: "🏠"
|
||||||
|
- title: "code"
|
||||||
|
summary: "Curated catalog of my GitHub projects."
|
||||||
|
href: "https://code.moonweb.org"
|
||||||
|
emoji: "💻"
|
||||||
|
- title: "retro"
|
||||||
|
summary: "Physical retro hardware collection."
|
||||||
|
href: "https://retro.moonweb.org"
|
||||||
|
emoji: "🕹️"
|
||||||
|
- heading: "About me"
|
||||||
|
cards:
|
||||||
|
- title: "CV / stefankoelle.de"
|
||||||
|
summary: "Career, contact, and background."
|
||||||
|
href: "https://stefankoelle.de"
|
||||||
|
emoji: "👤"
|
||||||
|
- heading: "Time capsules"
|
||||||
|
cards:
|
||||||
|
- title: "28k8.moonweb.org"
|
||||||
|
summary: "tHE tEMPLE BBS Mailbox and 90s releases archive."
|
||||||
|
href: "https://28k8.moonweb.org"
|
||||||
|
emoji: "💾"
|
||||||
|
- title: "www.moonweb.org"
|
||||||
|
summary: "The 2000s internet projects, networking and engineering."
|
||||||
|
href: "https://www.moonweb.org"
|
||||||
|
emoji: "🌐"
|
||||||
|
---
|
||||||
|
{% include "card-grid.njk" %}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
title: "Backup Strategy"
|
||||||
|
section: "infra"
|
||||||
|
parent: "/"
|
||||||
|
description: "Layered 3-2-1 backup strategy covering NAS snapshots, offsite copies, and cloud storage."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<h1>Backup Strategy</h1>
|
||||||
|
<div class="detail-content">
|
||||||
|
|
||||||
|
<p>The homelab uses a layered backup approach, split into two independent
|
||||||
|
tracks: NAS user content, and Proxmox VM/LXC backups. Both follow a
|
||||||
|
3-2-1-style rule — multiple copies, multiple media types, at least one
|
||||||
|
copy on a different machine.</p>
|
||||||
|
|
||||||
|
<h2>NAS user content — four layers</h2>
|
||||||
|
<table>
|
||||||
|
<tr><th>Layer</th><th>Target</th><th>Frequency</th><th>Purpose</th></tr>
|
||||||
|
<tr><td>Snapshots</td><td>Synology, built-in</td><td>Hourly</td><td>Fast recovery of accidentally deleted files</td></tr>
|
||||||
|
<tr><td>Local backup</td><td>USB drive attached to the NAS</td><td>Daily</td><td>Local versioned backup, independent of the NAS disks</td></tr>
|
||||||
|
<tr><td>Offsite sync</td><td>External drive on a Raspberry Pi in a different room</td><td>Weekly</td><td>Geographically separated copy within the flat</td></tr>
|
||||||
|
<tr><td>Cloud archive</td><td>Cloud storage</td><td>Continuous</td><td>Off-site copy for worst-case scenarios (fire, theft)</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2>Proxmox VM/LXC backups — three copies, two machines</h2>
|
||||||
|
<table>
|
||||||
|
<tr><th>Copy</th><th>Target</th><th>Frequency</th></tr>
|
||||||
|
<tr><td>1 — original</td><td>Proxmox host itself</td><td>Daily</td></tr>
|
||||||
|
<tr><td>2 — primary offsite</td><td>Synology NAS (different machine)</td><td>Daily, right after copy 1</td></tr>
|
||||||
|
<tr><td>3 — secondary offsite</td><td>Raspberry Pi backup target, different room</td><td>Daily (skipped one day/week to avoid network contention with the weekly NAS sync)</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2>Design principles</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Backups always cross at least one machine boundary — nothing is considered "backed up" if it only lives on the source machine's local disk.</li>
|
||||||
|
<li>Weekly and daily jobs are scheduled to avoid saturating the internal power-line network link used by the offsite backup target.</li>
|
||||||
|
<li>All scheduled jobs ping a dead-man's-switch monitoring service; a missed backup triggers an alert.</li>
|
||||||
|
<li>Power to the offsite drive is only switched on for the duration of its weekly sync, to save power and extend disk life.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
---
|
||||||
|
title: "Xubuntu Dev VM"
|
||||||
|
section: "infra"
|
||||||
|
parent: "/"
|
||||||
|
description: "A disposable, daily-backed-up Xubuntu VM for AI-assisted, remote development via SSH and Chrome Remote Desktop."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<h1>Xubuntu Dev VM</h1>
|
||||||
|
<div class="detail-content">
|
||||||
|
|
||||||
|
<p>This is a Xubuntu virtual machine on <a href="/proxmox/">Proxmox pve2</a> in the study that serves as my main development environment, replacing the need for a dedicated physical workstation. It's designed to be lightweight, remotely accessible from anywhere, and — most importantly — completely disposable, which is what makes it a safe place to let AI coding agents operate with far more autonomy than I'd otherwise allow.</p>
|
||||||
|
|
||||||
|
<h2>Why a disposable VM is the biggest advantage</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Daily backups as a safety net</strong> — the VM is backed up once a day through Proxmox. If an AI agent breaks the system, misconfigures something critical, or deletes the wrong files, I simply restore yesterday's snapshot rather than manually debugging the damage.</li>
|
||||||
|
<li><strong>No irreplaceable local data</strong> — nothing important lives only on this VM. All actual project code lives in Git repositories, so after a restore I just <code>git pull</code> everything back and I'm exactly where I left off, minus whatever the agent broke.</li>
|
||||||
|
<li><strong>Higher trust, more autonomy for AI agents</strong> — because the worst-case outcome is "restore a snapshot and re-pull," I can give coding agents like OpenCode much broader permissions on this machine than I would on a system holding unique, unbacked-up state. The blast radius of a mistake is capped at "lose a few hours of uncommitted work," never at "lose data."</li>
|
||||||
|
<li><strong>Cheap to rebuild from scratch</strong> — if the VM ever gets into a truly bad state, standing up a fresh Xubuntu VM and re-cloning repos is a short, well-understood process rather than an emergency.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Remote workflow via SSH</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong><a href="https://herdr.dev/">Herdr</a></strong> — manages my development workspaces over SSH, letting me organize and switch between multiple project contexts on the VM without manually juggling terminal sessions.</li>
|
||||||
|
<li><strong><a href="https://opencode.ai/de">OpenCode</a></strong> — the AI coding agent I run inside these workspaces; it's the main reason the VM's disposability matters, since it can act with elevated freedom knowing any damage is trivially reversible.</li>
|
||||||
|
<li><strong><a href="https://github.com/jesseduffield/lazygit">lazygit</a></strong> — terminal UI for Git, used for fast commit/branch/diff workflows directly inside the SSH session without leaving the terminal.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Graphical fallback via Chrome Remote Desktop</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Full desktop access</strong> — when a GUI IDE is preferable to terminal-based editing, Chrome Remote Desktop connects straight into the Xubuntu desktop from any device.</li>
|
||||||
|
<li><strong>WebStorm</strong> — used for TypeScript projects that benefit from a full IDE (refactoring tools, debugging, integrated test runners).</li>
|
||||||
|
<li><strong>PyCharm</strong> — used the same way for Python projects.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p class="redacted-note">This setup deliberately treats the dev VM as ephemeral infrastructure, not a pet — the philosophy is: keep state in Git, keep the VM replaceable, and let daily backups absorb the risk of giving an AI agent real write access to the system.</p>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
---
|
||||||
|
title: "Docker Container Strategy"
|
||||||
|
section: "infra"
|
||||||
|
parent: "/"
|
||||||
|
description: "How self-hosted services are organized across three Docker hosts on Proxmox and Synology, using Docker Compose as the single source of truth."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<h1>Docker Container Strategy</h1>
|
||||||
|
<div class="detail-content">
|
||||||
|
|
||||||
|
<p>My homelab runs almost entirely on Docker, spread across three dedicated Debian VMs instead of installing containers directly on bare metal or on the NAS operating system itself. Each VM is a clean, disposable Docker host that can be snapshotted and backed up as a whole through Proxmox, and every service is defined declaratively as a Docker Compose file living under <code>/docker-data/compose/<service>/</code>. This keeps the setup portable, reproducible, and easy to document — if a host dies, restoring a VM snapshot brings every container definition back with it.</p>
|
||||||
|
|
||||||
|
<h2>The three Docker hosts</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>docker-host-pve</strong> — Debian VM on the main Proxmox server (PVE). This is the primary target for almost all container deployments, including my own images pulled from GHCR. Fully covered by Proxmox VM backups, so the whole host can be restored in one shot.</li>
|
||||||
|
<li><strong>docker-host-nas</strong> — Debian VM running directly on the Synology (via Virtual Machine Manager). Identical setup and folder structure to the PVE host, but used specifically for containers that need direct access to NAS storage volumes, such as media-processing tools that read and write large file libraries.</li>
|
||||||
|
<li><strong>docker-host-pve2</strong> — Debian VM on the secondary Proxmox server (pve2), which lives in the study and is treated as a pure test/dev environment. New containers and compose setups get trialed here before being promoted to docker-host-pve.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Standardized layout</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>/docker-data/compose/</strong> — one subfolder per service, each containing its own <code>docker-compose.yml</code> (and <code>.env</code> where needed).</li>
|
||||||
|
<li><strong>/docker-data/volumes/</strong> — persistent container data, kept outside the compose folders so it survives redeploys.</li>
|
||||||
|
<li><strong>/docker-data/backups/</strong> — local backup staging before data is pulled into the wider backup chain.</li>
|
||||||
|
<li><strong>Same convention everywhere</strong> — because all three hosts follow this identical structure, moving a service between hosts or rebuilding a host from scratch is just a matter of copying the compose folder and running <code>docker compose up -d</code>.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Own GitHub projects</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>icloud-contacts-sync</strong> — syncs contact data between iCloud and other systems.</li>
|
||||||
|
<li><strong>calendar-sync</strong> — keeps calendars synchronized across sources.</li>
|
||||||
|
<li><strong>mvg-departures</strong> — pulls Munich public transport (MVG) departure data for local dashboards.</li>
|
||||||
|
<li><strong>wetter-api</strong> — a small weather API/worker pair, built from a custom GHCR image and updated automatically via Watchtower.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Infrastructure containers</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>mosquitto-mqtt</strong> — central MQTT broker for smart-home and sensor messaging.</li>
|
||||||
|
<li><strong>grafana, prometheus, influxdb</strong> — the monitoring and metrics stack for dashboards and time-series data.</li>
|
||||||
|
<li><strong>healthchecks, uptime-kuma, cadvisor</strong> — service and container health/uptime monitoring.</li>
|
||||||
|
<li><strong>dyndns-updater</strong> — keeps DNS pointed at the home connection; my own project, see <a href="https://github.com/skoelle/dyndns-updater">skoelle/dyndns-updater</a> on GitHub.</li>
|
||||||
|
<li><strong>watchtower</strong> — automatically checks for and applies container image updates on a daily schedule.</li>
|
||||||
|
<li><strong>authelia, nginx-proxy-manager</strong> — authentication layer and reverse proxy for exposing internal services safely.</li>
|
||||||
|
<li><strong>postfix</strong> — internal mail relay used by other containers (e.g. Watchtower notifications) to send email.</li>
|
||||||
|
<li><strong>portainer</strong> — web UI for managing containers across hosts.</li>
|
||||||
|
<li><strong>exporters</strong> — Prometheus exporters for Fritz!Box, Zyxel switches, Nginx, and SNMP devices, feeding the Grafana/Prometheus stack.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Smart home containers</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Home Assistant</strong> — only used for homematicIP integration to mqtt.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Application containers</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>SearXNG</strong> — self-hosted, privacy-respecting metasearch engine.</li>
|
||||||
|
<li><strong>Open WebUI</strong> — chat interface for locally hosted LLMs.</li>
|
||||||
|
<li><strong>Stirling PDF</strong> — self-hosted PDF toolkit for merging, converting and editing documents.</li>
|
||||||
|
<li><strong>ArchiveBox</strong> — personal web archiving of bookmarked pages.</li>
|
||||||
|
<li><strong>TubeSync / TubeArchivist [NAS]</strong> — download and archive YouTube content directly onto NAS storage; these run on docker-host-nas specifically because they need direct volume access to the Synology's disks.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Development tools</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Gitea</strong> — self-hosted Git server acting as a mirror of my GitHub repositories, giving me a local fallback and faster internal access.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
module.exports = function (eleventyConfig) {
|
||||||
|
eleventyConfig.addPassthroughCopy({ "shared/theme-infra.css": "theme.css" });
|
||||||
|
eleventyConfig.addPassthroughCopy({ "shared/base.css": "shared-base.css" });
|
||||||
|
eleventyConfig.addPassthroughCopy({ "shared/favicon/infra.svg": "favicon.svg" });
|
||||||
|
|
||||||
|
return {
|
||||||
|
dir: {
|
||||||
|
input: "infra",
|
||||||
|
includes: "../shared/_includes",
|
||||||
|
output: "dist/infra"
|
||||||
|
},
|
||||||
|
serverOptions: {
|
||||||
|
host: "0.0.0.0",
|
||||||
|
port: 8082
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
---
|
||||||
|
title: "Infra"
|
||||||
|
section: "infra"
|
||||||
|
description: "Homelab infrastructure overview: Proxmox hosts, Synology NAS, Docker VMs and Setup-Guides."
|
||||||
|
layout: base.njk
|
||||||
|
sections:
|
||||||
|
- heading: "Plattform"
|
||||||
|
cards:
|
||||||
|
- title: "🖥️ Proxmox host (PVE)"
|
||||||
|
summary: "Hosting environment for running production VMs and LXC containers for the homelab."
|
||||||
|
href: "/proxmox/"
|
||||||
|
- title: "💾 Synology DS918+"
|
||||||
|
summary: "NAS running Docker services and acting as the primary backup repository."
|
||||||
|
href: "/synology/"
|
||||||
|
- title: "🐳 Docker VMs (Debian)"
|
||||||
|
summary: "Primary target for new Docker/Compose services, running the monitoring exporters."
|
||||||
|
href: "/docker/"
|
||||||
|
- title: "📦 LxC Container"
|
||||||
|
summary: "Pi-hole, Ubuntu-Worker and MariaDB running on slim LxC Containers."
|
||||||
|
href: "/lxc/"
|
||||||
|
- heading: "Strategies & Setup-Guides"
|
||||||
|
cards:
|
||||||
|
- title: "🔄 Backup strategy"
|
||||||
|
summary: "Layered backup approach covering NAS user content and VM/LXC backups across multiple locations."
|
||||||
|
href: "/backup-strategy/"
|
||||||
|
- title: "📊 Prometheus/Grafana"
|
||||||
|
summary: "Central metrics collection and dashboards for hosts, containers, and hardware."
|
||||||
|
href: "/monitoring/"
|
||||||
|
- title: "💻 Dev environment"
|
||||||
|
summary: "A disposable, daily-backed-up Xubuntu VM for AI-assisted, remote development via SSH and Chrome Remote Desktop."
|
||||||
|
href: "/dev-environment/"
|
||||||
|
---
|
||||||
|
{% include "card-grid.njk" %}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
---
|
||||||
|
title: "LXC Container Strategy"
|
||||||
|
section: "infra"
|
||||||
|
parent: "/"
|
||||||
|
description: "Overview of the lightweight LXC containers running on the main Proxmox host, and why they are LXC instead of Docker."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<h1>LXC Container Strategy</h1>
|
||||||
|
<div class="detail-content">
|
||||||
|
|
||||||
|
<p>Alongside the Docker-based setup, I run a small number of Linux Containers (LXC) directly on the main Proxmox host (PVE). An LXC is an OS-level virtualization container — unlike a Docker container, which packages a single application and its dependencies, an LXC behaves like a full lightweight Linux system with its own init process, systemd services and package manager, but without the overhead of a full virtual machine. Proxmox manages LXCs natively as first-class citizens, right next to VMs, with their own snapshotting, backup and resource-limit tooling. I use far fewer LXCs than Docker containers, reserving them for cases where a persistent, OS-like environment or tight integration with Proxmox itself makes more sense than a containerized app.</p>
|
||||||
|
|
||||||
|
<h2>Why LXC instead of Docker here</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Native Proxmox integration</strong> — LXCs show up directly in the Proxmox UI with their own resource graphs, backup jobs and snapshots, without needing a Docker host VM in between.</li>
|
||||||
|
<li><strong>Lower overhead than a VM</strong> — an LXC shares the host kernel, so it starts almost instantly and uses less RAM/CPU than a full Debian VM, while still feeling like a real, persistent Linux machine.</li>
|
||||||
|
<li><strong>Better fit for system-level services</strong> — some services (like a DNS resolver or a database server) benefit from being a stable, always-on OS process with predictable networking, rather than an ephemeral, frequently-redeployed container.</li>
|
||||||
|
<li><strong>Simplicity for infrequently-changed services</strong> — these three services rarely change their configuration, so the extra flexibility of Compose-based redeployment isn't needed; a straightforward apt-installed service is easier to reason about long-term.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Pi-hole</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Purpose</strong> — network-wide DNS sinkhole and ad/tracker blocking for every device on the home network.</li>
|
||||||
|
<li><strong>Why LXC</strong> — DNS resolution needs to be rock-solid and always reachable at a fixed IP; running it as a lean, dedicated LXC avoids depending on the Docker host VM being up, and keeps it isolated from Docker networking quirks that could interfere with DNS.</li>
|
||||||
|
<li><strong>Advantage</strong> — near-zero overhead, boots in seconds after a Proxmox host reboot, and its stability is decoupled from whatever is happening on the Docker hosts.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Ubuntu-Worker</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Purpose</strong> — a general-purpose Ubuntu LXC used as a flexible worker/utility machine for ad-hoc scripts, cron jobs and small maintenance tasks that don't warrant their own container image.</li>
|
||||||
|
<li><strong>Why LXC</strong> — it needs to behave like a normal Linux box (full package manager, cron, shell access) rather than a single-purpose containerized app, which makes an LXC a much better fit than wrapping everything in Docker.</li>
|
||||||
|
<li><strong>Advantage</strong> — quick to spin up, snapshot and roll back via Proxmox, and useful as a lightweight sandbox for testing shell scripts or automation before productionizing them elsewhere.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>MariaDB</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Purpose</strong> — central relational database instance, currently used by services such as nginx-proxy-manager, reachable over the network at a fixed internal IP.</li>
|
||||||
|
<li><strong>Why LXC</strong> — a database benefits from direct, persistent disk access and predictable performance without the extra storage-driver indirection of Docker volumes; running it as an unprivileged LXC keeps it isolated while still feeling like a native MariaDB install.</li>
|
||||||
|
<li><strong>Advantage</strong> — simple backup via <code>mysqldump</code>, straightforward remote access configuration (bind-address, dedicated service users per consuming application), and one central database that multiple Docker containers on other hosts can connect to over the network instead of each running their own database container.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p class="redacted-note">All three LXCs run exclusively on the main Proxmox host (PVE), not on pve2 or the Synology — they're intentionally kept centralized since they're few in number and don't need the multi-host redundancy that the Docker setup has. IPs, credentials and specific VMIDs are omitted here; only the reasoning behind the LXC-vs-Docker choice is shown.</p>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
title: "Monitoring"
|
||||||
|
section: "infra"
|
||||||
|
parent: "/"
|
||||||
|
description: "Prometheus and Grafana monitoring stack for hosts, containers, and hardware metrics."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<h1>Monitoring</h1>
|
||||||
|
<div class="detail-content">
|
||||||
|
|
||||||
|
<p>Prometheus and Grafana form the central monitoring stack for the whole
|
||||||
|
homelab, running as Docker containers on the NAS.</p>
|
||||||
|
|
||||||
|
<h2>What's monitored</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Proxmox host metrics</strong> — a PVE exporter reports node-level and per-VM/LXC CPU, memory, and disk metrics into a dedicated Grafana dashboard.</li>
|
||||||
|
<li><strong>Host metrics everywhere</strong> — a node exporter runs on the NAS, the Docker host, and every Raspberry Pi, feeding basic CPU/RAM/disk/network stats.</li>
|
||||||
|
<li><strong>Container metrics</strong> — cAdvisor runs on both the Docker host and the NAS, breaking down resource usage per container.</li>
|
||||||
|
<li><strong>Network hardware via SNMP</strong> — an SNMP exporter polls a network-attached printer for toner level, page count, and status, proving the same pattern works for any SNMP-capable device.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Dashboards</h2>
|
||||||
|
<table>
|
||||||
|
<tr><th>Dashboard</th><th>Focus</th></tr>
|
||||||
|
<tr><td>Proxmox monitoring (extended)</td><td>Node-level and per-guest VM/LXC metrics</td></tr>
|
||||||
|
<tr><td>Docker container monitoring</td><td>Per-container CPU/memory/network/disk</td></tr>
|
||||||
|
<tr><td>Network printer</td><td>Toner level, page count, online/offline status via SNMP</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2>Open items</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Alerting rules are not yet configured for most services.</li>
|
||||||
|
<li>The secondary (test) Proxmox host isn't monitored yet — it's usually powered off.</li>
|
||||||
|
<li>Retention policy for Prometheus data hasn't been tuned.</li>
|
||||||
|
<li>Monitoring data itself isn't currently backed up.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
title: "Proxmox Virtualization"
|
||||||
|
section: "infra"
|
||||||
|
parent: "/"
|
||||||
|
description: "What Proxmox VE is, the hardware it runs on in my homelab, its key advantages, and a short setup walkthrough."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<h1>Proxmox Virtualization</h1>
|
||||||
|
<div class="detail-content">
|
||||||
|
|
||||||
|
<p>Proxmox VE (Virtual Environment) is a free, open-source virtualization platform that combines KVM-based full virtual machines with lightweight LXC containers in a single web-managed system. It's built on top of Debian, includes ZFS storage management, backup/restore, clustering and monitoring out of the box, and is the foundation my entire homelab runs on — both the <a href="/docker/">Docker</a> hosts and the standalone <a href="/lxc/">LXC services</a> live as guests on top of it.</p>
|
||||||
|
|
||||||
|
<h2>Hardware</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>PVE (main server)</strong> — a Beelink Mini-PC, running as the primary Proxmox host. It has a 500GB SSD for VM and container storage, and connects to the network via the server-room Zyxel switch.</li>
|
||||||
|
<li><strong>pve2 (test server)</strong> — a second Proxmox host, physically located in the study and connected via the OpenWRT-side Zyxel switch. It's dedicated purely to testing and development, running the third <a href="/docker/">Docker host VM (docker-host-pve2)</a> plus experimental setups without risking the production environment.</li>
|
||||||
|
<li><strong>Backups</strong> — nightly VM backups from PVE run at 00:30 to an NFS share on the Synology (<code>nas:/volume1/backup</code>), with retention of the last 3 daily, 3 monthly and 1 yearly backup.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Advantages</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Mixed virtualization</strong> — full KVM virtual machines and lightweight LXC containers run side by side on the same host, so I can pick whichever fits a service best.</li>
|
||||||
|
<li><strong>Web-based management</strong> — the entire cluster, its VMs, containers, storage and backups are managed from one browser UI via Authelia/Nginx, no separate tooling needed.</li>
|
||||||
|
<li><strong>Built-in ZFS support</strong> — snapshots, checksumming and pool management are native, making disk setup and VM protection straightforward.</li>
|
||||||
|
<li><strong>Native backup/restore</strong> — scheduled backups to an external NFS target are configured directly in Proxmox, without third-party backup software.</li>
|
||||||
|
<li><strong>No licensing cost</strong> — the platform is fully usable without a subscription by switching to the no-subscription repository, which matters for a homelab that isn't running on enterprise support contracts.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Open-source edition</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Proxmox VE Community/no-subscription</strong> — the enterprise repositories are disabled and the host is switched to the free "no-subscription" package repository, giving full functionality identical to the paid tiers minus vendor support and the enterprise-only repo.</li>
|
||||||
|
<li><strong>License</strong> — Proxmox VE itself is open source (AGPLv3); the subscription only adds official support and access to the more conservative enterprise update channel.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Setup process</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Bootstrap</strong> — on new Beelink hardware, an initial OS (Windows) needs to be cleared first; entering the BIOS (DEL key) to set the boot order to USB-first allows booting the Proxmox installer.</li>
|
||||||
|
<li><strong>Base install</strong> — Proxmox VE installed directly from ISO, then updated after disabling the enterprise repo and enabling the no-subscription repo.</li>
|
||||||
|
<li><strong>Storage</strong> — an additional SSD is partitioned (GPT via <code>parted</code>) and turned into a ZFS pool through the Disks → ZFS section of the web UI.</li>
|
||||||
|
<li><strong>Networking/backup target</strong> — an NFS share from the Synology is mounted for backup storage, and a nightly backup job is scheduled per VM.</li>
|
||||||
|
<li><strong>Guest creation</strong> — VMs (like the Debian Docker-host VMs) and LXCs (Pi-hole, Ubuntu-Worker, MariaDB) are created through the web UI, with ISO images uploaded to local storage beforehand.</li>
|
||||||
|
</ul>
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
title: "Synology NAS"
|
||||||
|
section: "infra"
|
||||||
|
parent: "/"
|
||||||
|
description: "Model, storage setup and current role of the Synology NAS in the homelab after the late-2025 cleanup."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<h1>Synology NAS</h1>
|
||||||
|
<div class="detail-content">
|
||||||
|
|
||||||
|
<p>The NAS at the center of the homelab is a Synology DS918+, a 4-bay desktop NAS originally released in 2018. It runs Synology's DSM operating system and uses SHR (Synology Hybrid RAID) across its four drive bays, mixing different drive capacities while still keeping a full mirror-equivalent level of redundancy. After a focused cleanup pass at the end of 2025, the NAS was stripped down to only the packages and containers that are actually in active use, rather than years of accumulated, half-used software.</p>
|
||||||
|
|
||||||
|
<h2>Storage</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Bays</strong> — 4 drive slots, populated with a mix of large-capacity HDDs rather than a single uniform set, which SHR was specifically chosen to support.</li>
|
||||||
|
<li><strong>RAID type</strong> — SHR (Synology Hybrid RAID), giving single-drive fault tolerance while allowing disks of different sizes to be mixed and later upgraded one at a time without rebuilding the whole array from scratch.</li>
|
||||||
|
<li><strong>Snapshots</strong> — Btrfs snapshot replication is used for file versioning instead of the classic recycle bin, taken hourly with a retention schedule of 7 days, 3 weeks, 3 months and 1 year.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>What runs on it today</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Docker host VM</strong> — a dedicated Debian VM (docker-host-nas) runs on the NAS's virtualization layer, hosting containers that specifically need direct access to NAS storage volumes.</li>
|
||||||
|
<li><strong>File storage & backup target</strong> — the core role of the NAS remains classic network storage, plus acting as the backup destination for scheduled Proxmox VM backups from the rest of the homelab.</li>
|
||||||
|
<li><strong>Legacy packages removed</strong> — unused DSM packages (like a web/photo/media package, an old drive-sync service and unused indexing) were uninstalled to reduce background load and disk writes.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Main strengths</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Central storage hub</strong> — single source of truth for files and backups that every other host in the network (Proxmox VMs, exporters, workstations) can write to or read from.</li>
|
||||||
|
<li><strong>Built-in snapshotting</strong> — Btrfs-based snapshots give point-in-time recovery without the overhead of a separate versioning package.</li>
|
||||||
|
<li><strong>Flexible RAID</strong> — SHR allows disks to be swapped and capacity to grow over time without needing matched drive sizes from day one.</li>
|
||||||
|
<li><strong>Lightweight virtualization</strong> — running a single purpose-built Docker VM on the NAS keeps storage-adjacent workloads close to the data, without turning the NAS itself into a general-purpose application server.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
Generated
+1693
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "moonweb-site",
|
||||||
|
"version": "0.2.0",
|
||||||
|
"private": true,
|
||||||
|
"description": "Monorepo for hub/infra/smarthome/code/retro (Eleventy, static, deployed to Cloudflare Pages)",
|
||||||
|
"scripts": {
|
||||||
|
"dev:hub": "npx @11ty/eleventy --config=hub/eleventy.config.js --serve --port=8081",
|
||||||
|
"dev:infra": "npx @11ty/eleventy --config=infra/eleventy.config.js --serve --port=8082",
|
||||||
|
"dev:smarthome": "npx @11ty/eleventy --config=smarthome/eleventy.config.js --serve --port=8083",
|
||||||
|
"dev:code": "npx @11ty/eleventy --config=code/eleventy.config.js --serve --port=8084",
|
||||||
|
"dev:retro": "npx @11ty/eleventy --config=retro/eleventy.config.js --serve --port=8085",
|
||||||
|
"dev": "npm run dev:hub & npm run dev:infra & npm run dev:smarthome & npm run dev:code & npm run dev:retro",
|
||||||
|
"build:hub": "npx @11ty/eleventy --config=hub/eleventy.config.js",
|
||||||
|
"build:infra": "npx @11ty/eleventy --config=infra/eleventy.config.js",
|
||||||
|
"build:smarthome": "npx @11ty/eleventy --config=smarthome/eleventy.config.js",
|
||||||
|
"build:code": "npx @11ty/eleventy --config=code/eleventy.config.js",
|
||||||
|
"build:retro": "npx @11ty/eleventy --config=retro/eleventy.config.js",
|
||||||
|
"build": "npm run build:hub && npm run build:infra && npm run build:smarthome && npm run build:code && npm run build:retro"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@11ty/eleventy": "^3.1.6"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title: "Running AmigaOS in 2026: MiSTer FPGA vs Amiberry on Pi"
|
||||||
|
section: "retro"
|
||||||
|
description: "Two very different ways to get a fully working AmigaOS 3.2.3 desktop running today."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<a class="back-link" href="/">← Back to retro overview</a>
|
||||||
|
<h1>Running AmigaOS in 2026: MiSTer FPGA vs Amiberry on Pi</h1>
|
||||||
|
<div class="detail-content">
|
||||||
|
|
||||||
|
<p>AmigaOS 3.2.3 runs beautifully both on FPGA and on modern ARM boards — the question is which trade-offs you're willing to accept.</p>
|
||||||
|
|
||||||
|
<h2>Two Roads to the Same Workbench</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>MiSTer with the Minimig-AGA core</strong> — the most hardware-authentic option. Native resolution tops out around 800×600, with RTG (Picasso96) support pushing that up to 1080p. Timing accuracy and near-zero input latency make it the pick for demos and cycle-sensitive software.</li>
|
||||||
|
<li><strong>Amiberry on a Raspberry Pi</strong> — the flexible option. Running headless (no X11 desktop, straight from the console/KMS framebuffer) is the single biggest performance lever — the difference between a laggy desktop and a snappy 60fps Workbench.</li>
|
||||||
|
<li><strong>The HDF minefield</strong> — hard disk image files either carry an RDB partition table or they don't, and getting the geometry parameters (surfaces/sectors/reserved blocks) wrong produces the classic "Not a DOS disk in device DH0" error. Reformatting through HDToolBox from a fresh install is often faster than debugging geometry math.</li>
|
||||||
|
<li><strong>Picking a monitor</strong> — a 4:3 panel at native 1280×1024 (like an older NEC MultiSync) gives the most period-accurate Workbench experience; anything widescreen needs scaling compromises.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p class="redacted-note">Both setups now run side by side without any real winner — MiSTer for authenticity and demos, the Pi for tinkering and RTG resolutions.</p>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title: "Vampire V4, A600GS or MiSTer: Amiga Hardware Isn't a Simple Speed Race"
|
||||||
|
section: "retro"
|
||||||
|
description: "Why raw benchmark numbers are actively misleading when comparing modern Amiga hardware options."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<a class="back-link" href="/">← Back to retro overview</a>
|
||||||
|
<h1>Vampire V4, A600GS or MiSTer: Amiga Hardware Isn't a Simple Speed Race</h1>
|
||||||
|
<div class="detail-content">
|
||||||
|
|
||||||
|
<p>Trying to compare Amiga hardware options by benchmark numbers alone is a trap. A Dhrystone score from a real FPGA CPU implementation and an ARM chip's MIPS rating measure completely different things — and neither tells you much about which system will actually run your favourite demo correctly.</p>
|
||||||
|
|
||||||
|
<h2>The Contenders</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Vampire V4 Standalone</strong> — an FPGA-implemented 68080 CPU that's genuinely blisteringly fast (sub-second Workbench boots), but software compatibility suffers since the 68080 isn't a perfect 68060 clone. Best for games and demos, not for daily productivity.</li>
|
||||||
|
<li><strong>A600GS / A1200NG</strong> — ARM-based boards running Amiberry underneath, in either a complete mini-PC (A600GS) or a drop-in motherboard replacement with real floppy and CompactFlash support (A1200NG). Cheap, plug-and-play, but with occasional software compatibility quirks.</li>
|
||||||
|
<li><strong>MiSTer with the Minimig core</strong> — "loses" on paper to A600GS in raw MIPS numbers, but wins on emulation accuracy and software compatibility. Its D-Cache-on mode is faster still but introduces glitches, so it's best left off for stability.</li>
|
||||||
|
<li><strong>The real decision factor</strong> — not speed, but compatibility, stability, and how much you value hardware-accurate timing versus raw throughput.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p class="redacted-note">Conclusion after digging through all three: if you already own a working MiSTer, there's no compelling reason to chase the newer ARM boards for "better" performance — the practical difference is marginal, and MiSTer's broader core library wins in the long run.</p>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title: "The Atari Mega ST Fleet: Three Machines, Three Jobs"
|
||||||
|
section: "retro"
|
||||||
|
description: "How three Atari Mega ST computers ended up with completely different roles in a modern retro setup."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<a class="back-link" href="/">← Back to retro overview</a>
|
||||||
|
<h1>The Atari Mega ST Fleet: Three Machines, Three Jobs</h1>
|
||||||
|
<div class="detail-content">
|
||||||
|
|
||||||
|
<p>Somewhere along the way, "one Atari ST" turned into three Mega ST 4 machines plus a Mega ST 2. Rather than let them gather dust, each one got a clearly defined purpose — from untouched nostalgia piece to fully upgraded workstation.</p>
|
||||||
|
|
||||||
|
<h2>The Lineup</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>The heirloom machine</strong> — kept as close to factory-original as possible, running the stock TOS 1.04 and a classic floppy drive. Its job is to be a baseline reference: if something breaks on a modded machine, this one proves the fault isn't in the software.</li>
|
||||||
|
<li><strong>The "Low" machine</strong> — a bargain-bin Mega ST 4 rebuilt with a Cloudy/StormyCloud combo for TOS switching and extra FastRAM, a Gotek floppy emulator, an UltraSatan hard disk emulator, and a SidecarTridge multi-device for ROM, floppy, and Wi-Fi tricks. This is the demo- and game-machine, optimized for fast disk-image swapping rather than serious productivity.</li>
|
||||||
|
<li><strong>The "High" machine</strong> — the workstation build. A MonSTerBoard adds 8MB of AltRAM, IDE storage, and flashable TOS (currently TOS 2.06), running Geneva for real multitasking. A NetUSB adapter and original Atari keyboard/mouse round it out, with an ET4000 graphics card in the pipeline for higher resolutions.</li>
|
||||||
|
<li><strong>The spare Mega ST 2</strong> — a reserve and donor machine, kept in largely original condition, used to safely test upgrades before they go into the "real" machines.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p class="redacted-note">Fun fact: two of the "problem" machines turned out to have nothing wrong with the boards at all — a badly soldered CPU socket and a leftover blitter patch bridge were the actual culprits. Moral of the story: check your solder joints twice before blaming the silicon.</p>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title: "The 15kHz Problem: Finding a Monitor That Speaks Atari"
|
||||||
|
section: "retro"
|
||||||
|
description: "Why most modern monitors refuse to display Atari ST colour modes, and which ones actually work."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<a class="back-link" href="/">← Back to retro overview</a>
|
||||||
|
<h1>The 15kHz Problem: Finding a Monitor That Speaks Atari</h1>
|
||||||
|
<div class="detail-content">
|
||||||
|
|
||||||
|
<p>Atari ST colour modes (Low-Res and Medium-Res) run at a horizontal scan frequency of 15kHz — a frequency almost no modern VGA monitor supports, since anything made in the last two decades expects at least 30kHz. High-Res monochrome runs at roughly 35kHz and gets along fine with most displays, but colour gaming is a different story entirely.</p>
|
||||||
|
|
||||||
|
<h2>Panel Technology Matters More Than You'd Think</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>MVA/PVA panels</strong> — the surprise winners. Their flexible crystal alignment tolerates unusual 15kHz signals far better than standard panels. A NEC MultiSync LCD1970NXp with an MVA panel handles Medium-Res colour reliably, once you fine-tune the H.Size setting.</li>
|
||||||
|
<li><strong>IPS panels</strong> — a mixed bag. Some work (a widescreen Lenovo ThinkVision LT1952p reportedly fills the screen beautifully via an ST2VGA adapter), others don't, and there's no way to tell without testing.</li>
|
||||||
|
<li><strong>VA panels</strong> — a dead end. Modern VA displays like the Dell SE2722HX and SE2422HX have a minimum horizontal frequency of 30kHz, ruling out native 15kHz signals entirely.</li>
|
||||||
|
<li><strong>The universal fix</strong> — an Open Source Scan Converter (OSSC) upsamples the 15kHz signal for any HDMI monitor, at the cost of extra hardware and money.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p class="redacted-note">If you're chasing 15kHz authenticity, buy the specific panel model people have already confirmed working in the forums — don't trust a spec sheet that just says "supports VGA."</p>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title: "Getting a 40-Year-Old Computer Online: Atari ST Networking and BBS Access"
|
||||||
|
section: "retro"
|
||||||
|
description: "Connecting an Atari ST to the modern internet and to dial-up-era bulletin board systems, warts and all."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<a class="back-link" href="/">← Back to retro overview</a>
|
||||||
|
<h1>Getting a 40-Year-Old Computer Online: Atari ST Networking and BBS Access</h1>
|
||||||
|
<div class="detail-content">
|
||||||
|
|
||||||
|
<p>The Atari ST predates the consumer internet entirely, yet a surprising amount of retrofitted networking hardware exists to get one talking to a modern network — or to a bulletin board system straight out of the dial-up era.</p>
|
||||||
|
|
||||||
|
<h2>The Toolbox</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>STinG</strong> — the classic TCP/IP stack for TOS, still the backbone for anything network-related on an original machine, from Telnet sessions to FTP transfers.</li>
|
||||||
|
<li><strong>ESP32-based Wi-Fi modules</strong> — small serial-attached boards that emulate a modem over Wi-Fi. Results vary wildly between firmware projects: one variant handles HTTP downloads fine but can't do Telnet, another supports full modem emulation but drops connections unpredictably during longer BBS sessions.</li>
|
||||||
|
<li><strong>Terminal software quirks</strong> — ANSI art and special characters routinely render incorrectly in period terminal clients, an ongoing annoyance when connecting to text-heavy BBS systems.</li>
|
||||||
|
<li><strong>File transfer the easy way</strong> — a plain FTP client on the Atari talking to a NAS on the local network sidesteps most of the internet-connectivity headaches entirely, especially for moving disk images and software archives back and forth.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p class="redacted-note">If you're chasing BBS nostalgia on real hardware: budget far more patience for the networking layer than for the actual computer. The 1980s hardware is the easy part; convincing 2020s Wi-Fi and modern BBS ANSI dialects to cooperate is the real challenge.</p>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title: "Batocera in the Living Room: One Box, Every Retro System"
|
||||||
|
section: "retro"
|
||||||
|
description: "Why the living room emulation station takes a completely different approach than the FPGA rigs upstairs."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<a class="back-link" href="/">← Back to retro overview</a>
|
||||||
|
<h1>Batocera in the Living Room: One Box, Every Retro System</h1>
|
||||||
|
<div class="detail-content">
|
||||||
|
|
||||||
|
<p>Not every retro session calls for hardware-accurate FPGA timing. Sometimes you just want to grab a controller on the couch and pick something from a big menu — that's exactly what the Batocera station in the living room is for.</p>
|
||||||
|
|
||||||
|
<h2>What's Under the Hood</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Software emulation, not FPGA</strong> — Hatari for Atari ST, UAE for Amiga, DOSBox for DOS, MAME/FBA for arcade, plus the usual console emulators (NES, SNES, Mega Drive, PlayStation). It's the go-to place for Atari ST gaming in this setup, since MiSTer's own ST core is unreliable.</li>
|
||||||
|
<li><strong>ROM organisation</strong> — following the TOSEC standard and cleaning up collections with RomVault DAT files, aiming for "1G1R" (One Game One ROM) sets to keep launcher menus from turning into an unmanageable wall of duplicates.</li>
|
||||||
|
<li><strong>Curated sources</strong> — a well-known "scene DVD" collection covers Atari ST releases comprehensively; Amiga has no equally tidy equivalent, since TOSEC's Amiga archive is comprehensive but messy, mixing dozens of crack-group releases per game.</li>
|
||||||
|
<li><strong>Division of labour with MiSTer/MiST</strong> — Batocera wins on convenience and breadth, MiSTer/MiST win on accuracy and low-latency feel for serious sessions.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p class="redacted-note">If you're building a similar box: don't skip the DAT-file cleanup step. An unsorted TOSEC dump makes browsing physically painful once you cross a few thousand entries.</p>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
title: "The 486 DX2-66 Tower: An Original PC Living Alongside Its FPGA Twin"
|
||||||
|
section: "retro"
|
||||||
|
description: "Keeping an original Colani-design 486 tower running while a MiSTer core covers the same era digitally."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<a class="back-link" href="/">← Back to retro overview</a>
|
||||||
|
<h1>The 486 DX2-66 Tower: An Original PC Living Alongside Its FPGA Twin</h1>
|
||||||
|
<div class="detail-content">
|
||||||
|
|
||||||
|
<p>Long before Atari ST and Amiga took over the retro corner, a Vobis-Highscreen 486 DX2-66 in its distinctive Colani-designed tower case was the main PC. Decades later, it's kept around as both a museum piece and a genuine gaming machine, running alongside a MiSTer FPGA equivalent.</p>
|
||||||
|
|
||||||
|
<h2>Original Hardware vs FPGA Twin</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>The original tower</strong> — an Intel 486 DX2 @ 66MHz in its iconic Colani case, the go-to machine for early-90s classics like Doom, Wing Commander, Duke Nukem 3D, and Lemmings.</li>
|
||||||
|
<li><strong>MiSTer's AO486 core</strong> — a full FPGA-based 486 emulation, configurable across variants from 386SX all the way to 486DX4, using swappable virtual hard disk (VHD) images instead of a physical drive.</li>
|
||||||
|
<li><strong>Open project</strong> — DOS-era BBS access is next on the list, likely tackled via the MiSTer core first since its networking path is simpler than wrangling period-correct ISA network cards.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
module.exports = function (eleventyConfig) {
|
||||||
|
eleventyConfig.addPassthroughCopy({ "shared/theme-retro.css": "theme.css" });
|
||||||
|
eleventyConfig.addPassthroughCopy({ "shared/base.css": "shared-base.css" });
|
||||||
|
eleventyConfig.addPassthroughCopy({ "shared/favicon/retro.svg": "favicon.svg" });
|
||||||
|
|
||||||
|
return {
|
||||||
|
dir: {
|
||||||
|
input: "retro",
|
||||||
|
includes: "../shared/_includes",
|
||||||
|
output: "dist/retro"
|
||||||
|
},
|
||||||
|
serverOptions: {
|
||||||
|
host: "0.0.0.0",
|
||||||
|
port: 8085
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
title: "Why an ET4000 Graphics Card and a Mega ST Don't Mix (Yet)"
|
||||||
|
section: "retro"
|
||||||
|
description: "A cautionary tale about pushing 1980s power supplies past their limits with a modern-ish graphics card."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<a class="back-link" href="/">← Back to retro overview</a>
|
||||||
|
<h1>Why an ET4000 Graphics Card and a Mega ST Don't Mix (Yet)</h1>
|
||||||
|
<div class="detail-content">
|
||||||
|
|
||||||
|
<p>An ET4000 ISA graphics card, adapted to the Atari MegaBus, promises higher resolutions and more colors than the native Shifter chip can dream of. In practice, bolting one onto a heavily upgraded Mega ST turned into a lesson in power supply physics.</p>
|
||||||
|
|
||||||
|
<h2>What Went Wrong</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Power supply overload</strong> — the stock Mega ST PSU simply wasn't designed to feed a MonSTerBoard, an ET4000, and a MegaBus adapter at the same time. The 5V and 12V rails ended up right at their instability limit.</li>
|
||||||
|
<li><strong>The missing -12V</strong> — the ET4000 needs -12V for its output drivers, usually tapped from the RS-232 port, adding yet another drain on an already stressed supply.</li>
|
||||||
|
<li><strong>Heat with nowhere to go</strong> — the card runs hot, and the Mega ST case has zero provision for active cooling.</li>
|
||||||
|
<li><strong>Signal integrity issues</strong> — the ISA-to-MegaBus adapter introduces reflections at higher frequencies, and access to ET4000 RAM is roughly 65% slower than native ST RAM, with no hardware BitBLT to make up for it.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>The Practical Fallback</h2>
|
||||||
|
<p>FPGA cores on MiSTer and MiST only emulate the native ST Shifter — there's no ET4000 emulation on the horizon given the FPGA resources it would require. The real answer for higher resolutions turned out to be software: Hatari running in VDI mode on a Raspberry Pi 400, which comfortably delivers 1280×768 with far more colors and zero PSU drama.</p>
|
||||||
|
|
||||||
|
<p class="redacted-note">Lesson learned: "just add a graphics card" on 40-year-old hardware is never "just" anything. The ET4000 project isn't dead, but it's shelved until there's a properly beefed-up power supply and cooling solution to go with it.</p>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
---
|
||||||
|
title: "Retro"
|
||||||
|
section: "retro"
|
||||||
|
description: "Physical retro hardware collection — Raspberry Pi units, Pi 400, and vintage computing setups."
|
||||||
|
layout: base.njk
|
||||||
|
sections:
|
||||||
|
- heading: "Atari ST Corner"
|
||||||
|
cards:
|
||||||
|
- title: "🖥️ The Mega ST Fleet"
|
||||||
|
summary: "Three Mega ST 4 machines and a Mega ST 2, each with a clearly defined role from heirloom to workstation."
|
||||||
|
href: "/atari-mega-st-fleet/"
|
||||||
|
- title: "⚡ ET4000 Experiment"
|
||||||
|
summary: "Why bolting a graphics card onto a Mega ST overloads a 1980s power supply, and what actually solved the resolution problem."
|
||||||
|
href: "/et4000-experiment/"
|
||||||
|
- title: "📺 Monitor Compatibility"
|
||||||
|
summary: "The 15kHz problem explained, and which modern monitor panels actually cooperate with Atari colour modes."
|
||||||
|
href: "/atari-monitor-compatibility/"
|
||||||
|
- title: "🔌 Networking & BBS"
|
||||||
|
summary: "Getting an Atari ST online via STinG and Wi-Fi modem hardware, and dialing into classic bulletin board systems."
|
||||||
|
href: "/atari-networking-bbs/"
|
||||||
|
- heading: "Amiga Corner"
|
||||||
|
cards:
|
||||||
|
- title: "💾 AmigaOS on Modern Platforms"
|
||||||
|
summary: "Running AmigaOS 3.2.3 on MiSTer FPGA versus Amiberry on a Raspberry Pi, and the HDF pitfalls to avoid."
|
||||||
|
href: "/amiga-modern-platforms/"
|
||||||
|
- title: "🥧 The PiMiga Journey"
|
||||||
|
summary: "Three generations of the PiMiga distro compared, and why the newest release isn't automatically the best one."
|
||||||
|
href: "/pimiga-journey/"
|
||||||
|
- title: "⚔️ Vampire, A600GS or MiSTer"
|
||||||
|
summary: "Why comparing modern Amiga hardware by benchmark numbers alone is actively misleading."
|
||||||
|
href: "/amiga-hardware-landscape/"
|
||||||
|
- title: "📦 The A1200"
|
||||||
|
summary: "What's confirmed so far about Retro Games Ltd.'s upcoming Amiga 1200 console, ahead of its 2026 launch."
|
||||||
|
href: "/the-a1200/"
|
||||||
|
- heading: "FPGA & Emulation"
|
||||||
|
cards:
|
||||||
|
- title: "🧩 MiST and MiSTer"
|
||||||
|
summary: "Two FPGA boards with two very different personalities, and why the newer one doesn't win every category."
|
||||||
|
href: "/mist-mister-fpga/"
|
||||||
|
- title: "🛋️ Batocera in the Living Room"
|
||||||
|
summary: "The couch-friendly, software-emulation counterpart to the FPGA rigs, covering everything from Atari ST to arcade."
|
||||||
|
href: "/batocera-living-room/"
|
||||||
|
- title: "🕹️ Mini Arcades & Handhelds"
|
||||||
|
summary: "A buyer's guide to compact retro gaming devices, and how to spot a bootleg clone before you buy one."
|
||||||
|
href: "/mini-arcade-handheld-roundup/"
|
||||||
|
- heading: "DOS & PC"
|
||||||
|
cards:
|
||||||
|
- title: "💻 The 486 DX2-66 Tower"
|
||||||
|
summary: "An original Colani-design 486 tower kept running alongside its MiSTer AO486 FPGA equivalent."
|
||||||
|
href: "/dos-486-tower/"
|
||||||
|
- heading: "Overview"
|
||||||
|
cards:
|
||||||
|
- title: "🏠 The Retro Corner Today"
|
||||||
|
summary: "A room-by-room snapshot of every retro system currently active across the home."
|
||||||
|
href: "/retro-corner-snapshot/"
|
||||||
|
|
||||||
|
---
|
||||||
|
{% include "card-grid.njk" %}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title: "Mini Arcades and Handhelds: Sorting the Real Deal from the Bootlegs"
|
||||||
|
section: "retro"
|
||||||
|
description: "A buyer's guide to compact retro gaming devices, and why the cheapest option is almost never the right one."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<a class="back-link" href="/">← Back to retro overview</a>
|
||||||
|
<h1>Mini Arcades and Handhelds: Sorting the Real Deal from the Bootlegs</h1>
|
||||||
|
<div class="detail-content">
|
||||||
|
|
||||||
|
<p>The market for tiny plug-and-play arcade cabinets is full of traps. Some devices ship with genuinely licensed classics; others are dressed-up bootleg NES clones with unfamiliar Chinese ROM hacks pretending to be arcade games.</p>
|
||||||
|
|
||||||
|
<h2>What to Watch For</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>The bootleg trap</strong> — devices built around VT369-style chips (NES-compatible, not real arcade hardware) often bundle 200+ "games" that turn out to be unlicensed clones and homebrew hacks, cataloged in enthusiast bootleg-game wikis. No Pac-Man, no Donkey Kong, just look-alikes.</li>
|
||||||
|
<li><strong>Genuinely licensed compact options</strong> — devices built around real Namco or SNK licenses deliver actual classics (Pac-Man, Galaga, Dig Dug, dozens of Neo Geo titles) with premium displays, at a similar price point to the bootleg boxes.</li>
|
||||||
|
<li><strong>Expandable options</strong> — some cabinet-style devices accept extra MAME ROMs via SD card, letting you add real arcade classics after the fact — the one feature the bootleg boxes almost never offer without heavy soldering.</li>
|
||||||
|
<li><strong>The maximalist route</strong> — a Raspberry Pi running RetroPie, or a MiSTer FPGA setup, both scale to tens of thousands of games or hardware-accurate arcade cores respectively, at the cost of more setup effort.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p class="redacted-note">If a mini-arcade's game list reads like a string of titles you've genuinely never heard of, that's the tell. Real arcade classics have instantly recognisable names — bootlegs hide behind vague, generic-sounding ones.</p>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title: "MiST and MiSTer: Two FPGA Boxes, Two Very Different Personalities"
|
||||||
|
section: "retro"
|
||||||
|
description: "Why the newer, more powerful MiSTer isn't automatically the better choice for every retro system."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<a class="back-link" href="/">← Back to retro overview</a>
|
||||||
|
<h1>MiST and MiSTer: Two FPGA Boxes, Two Very Different Personalities</h1>
|
||||||
|
<div class="detail-content">
|
||||||
|
|
||||||
|
<p>Both boards run classic computers as hardware-accurate FPGA cores rather than software emulation, but living with them side by side reveals they're not interchangeable — each one is simply better at certain jobs.</p>
|
||||||
|
|
||||||
|
<h2>Division of Labour</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>MiST for Atari ST</strong> — older and more limited in scope than MiSTer, but its ST/STE core is rock solid. It's the default choice for anything Atari, paired with a network-capable configuration for file transfers.</li>
|
||||||
|
<li><strong>MiSTer for everything else</strong> — Amiga, DOS (via the AO486 core), and a huge library of other systems. Its Atari ST core, oddly, is the less reliable one of the two boards — reason enough to keep MiST around instead of retiring it.</li>
|
||||||
|
<li><strong>Native resolution ceiling</strong> — both boards only emulate the original Shifter chip on Atari ST: Low-Res (320×200), Medium-Res (640×200), and High-Res (640×400 mono). No ET4000-style graphics card emulation is on the table; a Falcon030 core with higher native resolutions has been rumoured for years without shipping.</li>
|
||||||
|
<li><strong>Known rough edges</strong> — accessory programs loading but not appearing on the desktop, and disabled XBOOT support, are still open items to chase down on the MiST side.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p class="redacted-note">The takeaway after months of use: don't assume the shinier, newer board wins every category. Sometimes the "older" FPGA implementation is simply the more mature one for a specific chipset.</p>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title: "PiMiga 3 to 5: Chasing the Perfect Amiga-on-a-Pi Distro"
|
||||||
|
section: "retro"
|
||||||
|
description: "Three generations of PiMiga later, here's what actually changed — and why newer isn't always better."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<a class="back-link" href="/">← Back to retro overview</a>
|
||||||
|
<h1>PiMiga 3 to 5: Chasing the Perfect Amiga-on-a-Pi Distro</h1>
|
||||||
|
<div class="detail-content">
|
||||||
|
|
||||||
|
<p>PiMiga bundles Amiberry, a full Amiga 1200 AGA emulation with Workbench 3.1, and thousands of pre-installed games into a ready-to-flash SD card image. Living through three major versions taught a few unexpected lessons.</p>
|
||||||
|
|
||||||
|
<h2>What Changed Across Versions</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>PiMiga 3.0 (2022)</strong> — the original recipe: Amiberry under DietPi with an XFCE desktop and the iGame v2 launcher. Now effectively retired, with no further updates.</li>
|
||||||
|
<li><strong>PiMiga 4.0 (2024)</strong> — brought multi-architecture support (ARM and Intel/AMD) and swapped in the MegaAGS launcher, with roughly 4,800 games pre-installed. Its biggest strength: MegaAGS ships with strong trainer/cheat support baked into its game profiles.</li>
|
||||||
|
<li><strong>PiMiga 5.0 (Dec 2025, final release)</strong> — the creator's last planned version. Switches back to the simpler iGame v2 launcher, drops in 600+ auto-switching backdrops, and adds native support for the newest Pi hardware — but loses much of the trainer-focused depth that made version 4 so useful for casual replay sessions.</li>
|
||||||
|
<li><strong>The real decision criterion</strong> — not "which is newest" but "do I care more about trainers/cheats (stick with v4's MegaAGS) or about the latest hardware support and polish (go with v5's iGame)". Running both on separate SD cards sidesteps the choice entirely.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p class="redacted-note">Also worth knowing: getting the display right on an older 4:3 monitor is almost never a Raspberry Pi config.txt problem — it's nearly always an Amiberry/X11 scaling setting. Check the emulator's Integer Scaling option before touching boot config files.</p>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title: "The Retro Corner Today: A Snapshot of the Whole Setup"
|
||||||
|
section: "retro"
|
||||||
|
description: "A tour through every retro system currently active across the home, room by room."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<a class="back-link" href="/">← Back to retro overview</a>
|
||||||
|
<h1>The Retro Corner Today: A Snapshot of the Whole Setup</h1>
|
||||||
|
<div class="detail-content">
|
||||||
|
|
||||||
|
<p>What started with childhood memories of an Atari ST and an Amiga 500 turned into a full-blown home retro-computing corner after 2020. Here's what's actually running right now, spread across two rooms plus a handful of portable devices.</p>
|
||||||
|
|
||||||
|
<h2>Room by Room</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>The office</strong> — home base for the serious gear: three Atari Mega ST 4 machines plus a Mega ST 2, a MiSTer FPGA covering Amiga/DOS/arcade cores, and a MiST FPGA dedicated to Atari ST. A Raspberry Pi 400 running Hatari and PiMiga rounds things out as the flexible, high-resolution alternative to hardware that can't quite keep up on screen quality.</li>
|
||||||
|
<li><strong>The living room</strong> — a Batocera box for casual, couch-friendly sessions across dozens of systems, from Atari ST to arcade to consoles, no FPGA timing precision required.</li>
|
||||||
|
<li><strong>On the move</strong> — a small collection of handheld emulation devices for retro gaming away from a screen.</li>
|
||||||
|
<li><strong>The focus, unmistakably</strong> — Amiga and Atari ST are the heart of the collection; everything else (DOS, arcade, handhelds) exists to fill in the gaps around those two platforms.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p class="redacted-note">The honest status update: several open items remain — documenting the exact handheld models, and waiting on the A1200 Mini pre-order. Retro computing, it turns out, is never really "done."</p>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
title: "The A1200: What We Know Before It Ships"
|
||||||
|
section: "retro"
|
||||||
|
description: "A close look at Retro Games Ltd.'s upcoming Amiga 1200 console, based on everything public so far."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<a class="back-link" href="/">← Back to retro overview</a>
|
||||||
|
<h1>The A1200: What We Know Before It Ships</h1>
|
||||||
|
<div class="detail-content">
|
||||||
|
|
||||||
|
<p>Retro Games Ltd. opened pre-orders for The A1200 in November 2025, with delivery slated for June 2026 at a suggested price of 189.99 Euro. As with their previous mini-consoles, official details have trickled out slowly — deliberately, it seems.</p>
|
||||||
|
|
||||||
|
<h2>What's Confirmed So Far</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Games</strong> — 25 titles pre-installed in total, with only 9 officially confirmed at time of writing: the Turrican trilogy, Defender of the Crown I & II, Beneath a Steel Sky, Lure of the Temptress, Ruff 'n' Tumble, and The Settlers II. The rest are being held back, likely on purpose, as a marketing drip-feed.</li>
|
||||||
|
<li><strong>Hardware essentials</strong> — a fully functional keyboard in the original design, a CD32-style gamepad, a tank-mouse replica, HDMI output, USB-A ports, and USB sideloading support for ADF/HDF game files.</li>
|
||||||
|
<li><strong>The mystery SD slot</strong> — a slot spotted at Gamescom 2025 in the position of the original floppy drive, widely speculated to be for SD-card-based game loading, but not officially confirmed.</li>
|
||||||
|
<li><strong>Expected internals</strong> — likely an Allwinner ARM SoC running Amiberry underneath (following the pattern of the earlier A500 Mini), rather than genuine 68EC020 silicon.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p class="redacted-note">Worth a heads-up if you're pre-ordering: not every regional shop went live on launch day, and lesser-known international retailers sometimes get product photos and details before the official site does. With a seven-month wait to delivery, there's no rush to jump on the very first listing you find.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# GitHub aggregator
|
||||||
|
|
||||||
|
Reads a `.moonweb.yml` file from each of the `skoelle` GitHub repos and
|
||||||
|
regenerates `code/_data/repos.json`. Run manually — not wired into CI.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export GITHUB_TOKEN=ghp_xxx
|
||||||
|
python aggregate.py
|
||||||
|
```
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
"""
|
||||||
|
GitHub aggregator for code.moonweb.org.
|
||||||
|
Reads a `.moonweb.yml` from the root of every public repo under `skoelle`
|
||||||
|
and writes the combined result to `code/_data/repos.json`. Manual, on
|
||||||
|
demand. Network calls are NOT executed as part of building this scaffold.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
GITHUB_USER = "skoelle"
|
||||||
|
API_ROOT = "https://api.github.com"
|
||||||
|
OUTPUT_PATH = os.path.join(
|
||||||
|
os.path.dirname(__file__), "..", "..", "code", "_data", "repos.json"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def gh_get(path: str):
|
||||||
|
token = os.environ.get("GITHUB_TOKEN")
|
||||||
|
req = urllib.request.Request(f"{API_ROOT}{path}")
|
||||||
|
if token:
|
||||||
|
req.add_header("Authorization", f"Bearer {token}")
|
||||||
|
req.add_header("Accept", "application/vnd.github+json")
|
||||||
|
with urllib.request.urlopen(req) as resp:
|
||||||
|
return json.loads(resp.read().decode("utf-8"))
|
||||||
|
|
||||||
|
|
||||||
|
def list_public_repos():
|
||||||
|
repos, page = [], 1
|
||||||
|
while True:
|
||||||
|
batch = gh_get(f"/users/{GITHUB_USER}/repos?per_page=100&page={page}")
|
||||||
|
if not batch:
|
||||||
|
break
|
||||||
|
repos.extend(r for r in batch if not r["private"] and not r["fork"])
|
||||||
|
page += 1
|
||||||
|
return repos
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_moonweb_yml(repo_name: str):
|
||||||
|
try:
|
||||||
|
data = gh_get(f"/repos/{GITHUB_USER}/{repo_name}/contents/.moonweb.yml")
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
content = base64.b64decode(data["content"]).decode("utf-8")
|
||||||
|
try:
|
||||||
|
import yaml
|
||||||
|
except ImportError:
|
||||||
|
print("PyYAML is required: pip install pyyaml", file=sys.stderr)
|
||||||
|
raise
|
||||||
|
return yaml.safe_load(content)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
entries = []
|
||||||
|
for repo in list_public_repos():
|
||||||
|
meta = fetch_moonweb_yml(repo["name"])
|
||||||
|
if not meta or meta.get("category") != "code":
|
||||||
|
continue
|
||||||
|
entries.append(meta)
|
||||||
|
|
||||||
|
entries.sort(key=lambda e: (e.get("subcategory", ""), e.get("title", "")))
|
||||||
|
|
||||||
|
os.makedirs(os.path.dirname(OUTPUT_PATH), exist_ok=True)
|
||||||
|
with open(OUTPUT_PATH, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(entries, f, indent=2, ensure_ascii=False)
|
||||||
|
f.write("\n")
|
||||||
|
|
||||||
|
print(f"Wrote {len(entries)} entries to {OUTPUT_PATH}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
title: "MVG Departures"
|
||||||
|
category: code
|
||||||
|
subcategory: "Web Apps"
|
||||||
|
status: active
|
||||||
|
stack: [Python, FastAPI]
|
||||||
|
hosted_on: "Docker Host Debian (PVE)"
|
||||||
|
summary: "Compact MVG/S-Bahn departure monitor with configurable stations."
|
||||||
|
repo_url: "https://github.com/skoelle/mvg-departures"
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>{{ title }} · moonweb.org</title>
|
||||||
|
<meta name="description" content="{{ description }}">
|
||||||
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Lobster&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="/shared-base.css">
|
||||||
|
<link rel="stylesheet" href="/theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="site-header">
|
||||||
|
<div class="site-banner">
|
||||||
|
<span class="site-title">{{ section }}.moonweb.org</span>
|
||||||
|
</div>
|
||||||
|
<nav class="site-switcher">
|
||||||
|
<a href="https://hub.moonweb.org">hub</a>
|
||||||
|
<a href="https://infra.moonweb.org">infra</a>
|
||||||
|
<a href="https://smarthome.moonweb.org">smarthome</a>
|
||||||
|
<a href="https://code.moonweb.org">code</a>
|
||||||
|
<a href="https://retro.moonweb.org">retro</a>
|
||||||
|
<a href="https://stefankoelle.de">cv</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
{% if parent %}<a class="back-link" href="{{ parent }}">← Back to {{ section }} overview</a>{% endif %}
|
||||||
|
{{ content | safe }}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer class="site-footer">
|
||||||
|
<p>Part of the <a href="https://hub.moonweb.org">moonweb.org</a> homelab.</p>
|
||||||
|
<p><a href="https://hub.moonweb.org/impressum/">Impressum</a></p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{% for section in sections %}
|
||||||
|
<section class="card-section">
|
||||||
|
<h2>{{ section.heading }}</h2>
|
||||||
|
<div class="card-grid">
|
||||||
|
{% for card in section.cards %}
|
||||||
|
<a class="card" href="{{ card.href }}">
|
||||||
|
<h3>{% if card.emoji %}{{ card.emoji }} {% endif %}{{ card.title }}</h3>
|
||||||
|
<p>{{ card.summary }}</p>
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endfor %}
|
||||||
+159
@@ -0,0 +1,159 @@
|
|||||||
|
/* shared/base.css — layout skeleton, shared by all home-section sites. */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--bg: #fafafa;
|
||||||
|
--text: #1a1a1a;
|
||||||
|
--card-bg: #ffffff;
|
||||||
|
--card-border: #e2e2e2;
|
||||||
|
--accent: #3b6ea5;
|
||||||
|
}
|
||||||
|
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-header { background: var(--accent); color: #fff; }
|
||||||
|
.site-banner { padding: 1.5rem 2rem 0.5rem; }
|
||||||
|
.site-title {
|
||||||
|
font-family: 'Lobster', cursive;
|
||||||
|
font-size: 3em;
|
||||||
|
color: #FFFFFF;
|
||||||
|
text-transform: lowercase;
|
||||||
|
display: block;
|
||||||
|
margin: 5px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-switcher {
|
||||||
|
display: flex;
|
||||||
|
gap: 1.25rem;
|
||||||
|
padding: 0.75rem 2rem;
|
||||||
|
background: rgba(0,0,0,0.12);
|
||||||
|
}
|
||||||
|
.site-switcher a { color: #fff; text-decoration: none; font-size: 0.9rem; opacity: 0.85; }
|
||||||
|
.site-switcher a:hover { opacity: 1; text-decoration: underline; }
|
||||||
|
|
||||||
|
main { max-width: 1100px; margin: 0 auto; padding: 2rem; }
|
||||||
|
|
||||||
|
.card-section { margin-bottom: 2.5rem; }
|
||||||
|
.card-section h2 {
|
||||||
|
font-size: 1rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
color: #666;
|
||||||
|
border-bottom: 1px solid var(--card-border);
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
display: block;
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid var(--card-border);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 1.25rem;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--text);
|
||||||
|
transition: box-shadow 0.15s ease, transform 0.15s ease;
|
||||||
|
}
|
||||||
|
.card:hover { box-shadow: 0 2px 10px rgba(0,0,0,0.08); transform: translateY(-1px); }
|
||||||
|
.card h3 { margin: 0 0 0.4rem; font-size: 1.05rem; color: var(--accent); }
|
||||||
|
.card p { margin: 0; font-size: 0.9rem; color: #555; }
|
||||||
|
|
||||||
|
.wip-badge {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #8a5b00;
|
||||||
|
background: #fff3d6;
|
||||||
|
border: 1px solid #f0d68a;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 0.15rem 0.5rem;
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.redacted-note {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: #666;
|
||||||
|
background: #f0f4f8;
|
||||||
|
border-left: 3px solid var(--accent);
|
||||||
|
padding: 0.6rem 0.9rem;
|
||||||
|
margin: 1.25rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-footer {
|
||||||
|
text-align: center;
|
||||||
|
padding: 2rem;
|
||||||
|
color: #888;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
.site-footer a { color: var(--accent); }
|
||||||
|
|
||||||
|
.detail-content {
|
||||||
|
max-width: 800px;
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid var(--card-border);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
.detail-content h1 {
|
||||||
|
color: var(--accent);
|
||||||
|
margin: 0 0 1.5rem;
|
||||||
|
padding-bottom: 0.75rem;
|
||||||
|
border-bottom: 2px solid var(--accent);
|
||||||
|
font-size: 1.8rem;
|
||||||
|
}
|
||||||
|
.detail-content h2 {
|
||||||
|
margin-top: 2rem;
|
||||||
|
padding-left: 0.75rem;
|
||||||
|
border-left: 3px solid var(--accent);
|
||||||
|
color: var(--text);
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
.detail-content table { border-collapse: collapse; width: 100%; margin: 1rem 0; font-size: 0.9rem; }
|
||||||
|
.detail-content th, .detail-content td {
|
||||||
|
border: 1px solid var(--card-border);
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.detail-content th {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.detail-content code { background: #f0f4f8; padding: 0.15rem 0.4rem; border-radius: 4px; font-size: 0.85em; }
|
||||||
|
.detail-content a {
|
||||||
|
color: var(--accent);
|
||||||
|
text-decoration: underline;
|
||||||
|
text-underline-offset: 2px;
|
||||||
|
}
|
||||||
|
.detail-content a:hover { color: var(--text); }
|
||||||
|
|
||||||
|
.back-link {
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: 1.25rem;
|
||||||
|
padding: 0.35rem 0.75rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--accent);
|
||||||
|
background: rgba(0, 0, 0, 0.04);
|
||||||
|
border: 1px solid var(--card-border);
|
||||||
|
border-radius: 6px;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: background 0.15s ease, border-color 0.15s ease;
|
||||||
|
}
|
||||||
|
.back-link:hover {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #fff;
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><text y=".9em" font-size="90">💻</text></svg>
|
||||||
|
After Width: | Height: | Size: 109 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><text y=".9em" font-size="90">🌙</text></svg>
|
||||||
|
After Width: | Height: | Size: 109 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><text y=".9em" font-size="90">🏗️</text></svg>
|
||||||
|
After Width: | Height: | Size: 112 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><text y=".9em" font-size="90">🕹️</text></svg>
|
||||||
|
After Width: | Height: | Size: 112 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><text y=".9em" font-size="90">🏠</text></svg>
|
||||||
|
After Width: | Height: | Size: 109 B |
@@ -0,0 +1 @@
|
|||||||
|
:root { --accent: #3E5098; }
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
:root { --accent: #3b6ea5; }
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
:root { --accent: #99333A; }
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
:root { --accent: #8a6d3b; }
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
:root { --accent: #1f8a8a; }
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
title: "AirPlay Audio"
|
||||||
|
section: "smarthome"
|
||||||
|
parent: "/"
|
||||||
|
description: "Multi-room AirPlay receivers with software volume boost on Raspberry Pi devices."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<h1>AirPlay Audio</h1>
|
||||||
|
<div class="detail-content">
|
||||||
|
|
||||||
|
<p>Two Raspberry Pis run AirPlay receivers so any Apple device in the flat
|
||||||
|
can stream audio to the kitchen or the bathroom without a dedicated
|
||||||
|
speaker app.</p>
|
||||||
|
|
||||||
|
<h2>Setup</h2>
|
||||||
|
<table>
|
||||||
|
<tr><th>Location</th><th>Hardware</th><th>AirPlay version</th><th>Runs as</th></tr>
|
||||||
|
<tr><td>Kitchen</td><td>Raspberry Pi 3B</td><td>AirPlay 2</td><td>Docker container (shairport-sync)</td></tr>
|
||||||
|
<tr><td>Bathroom</td><td>Raspberry Pi Zero W</td><td>AirPlay 1</td><td>Native systemd service (older OS, legacy repo)</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2>The volume-boost problem</h2>
|
||||||
|
<p>Both Pis' analog audio output is too quiet at full hardware volume, so
|
||||||
|
a software mixer stage (<code>+20 dB</code> via an ALSA softvol plugin) sits between
|
||||||
|
shairport-sync and the hardware. The hardware mixer itself is deliberately
|
||||||
|
left fixed at 100% / 0 dB, and shairport-sync only ever adjusts its own
|
||||||
|
internal software volume — this avoids the volume jumps and mixer
|
||||||
|
conflicts that show up when multiple layers all try to control loudness.</p>
|
||||||
|
|
||||||
|
<h2>Bathroom-specific integration</h2>
|
||||||
|
<p>The bathroom Pi also runs a small custom tool that starts an internet
|
||||||
|
radio stream automatically when its light sensor detects the light has
|
||||||
|
been switched on. A simple flag file signals whether AirPlay is currently
|
||||||
|
active, so the automatic radio stream politely stays off while someone is
|
||||||
|
actively AirPlaying — and resumes its normal behavior as soon as the
|
||||||
|
AirPlay session ends.</p>
|
||||||
|
|
||||||
|
<h2>Why this design</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Software-only volume control avoids ALSA mixer conflicts across two different loudness sources.</li>
|
||||||
|
<li>The flag-file handoff is trivial to implement and doesn't need a message broker for something this simple.</li>
|
||||||
|
<li>Docker on the kitchen Pi keeps that receiver easy to update; the bathroom Pi's older OS made a native install more reliable than fighting Docker on legacy hardware.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
title: "Balkon Pi"
|
||||||
|
section: "smarthome"
|
||||||
|
parent: "/"
|
||||||
|
description: "Balcony sensor, lighting, and audio automation running on a Raspberry Pi Zero W."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<h1>Balkon Pi</h1>
|
||||||
|
<div class="detail-content">
|
||||||
|
|
||||||
|
<p>A Raspberry Pi Zero W mounted near the balcony acts as a small,
|
||||||
|
self-contained smart-balcony controller: light control, an internet
|
||||||
|
radio player, sensor readings, and its own backup/monitoring, all on
|
||||||
|
very modest hardware.</p>
|
||||||
|
|
||||||
|
<h2>What it does</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Audio output</strong> via a DIY PWM-to-analog circuit on two GPIO pins — the Pi Zero has no built-in audio jack, so this is a well-known community workaround using a small RC filter.</li>
|
||||||
|
<li><strong>Physical controls</strong>: a push button for the balcony light and a toggle switch for music playback, read directly via GPIO.</li>
|
||||||
|
<li><strong>HTTP communication</strong> with an ESP32-based balcony light controller.</li>
|
||||||
|
<li><strong>Internet radio</strong> via a lightweight command-line audio player, controlled by a small custom tool ported from an earlier bathroom-Pi project.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Monitoring & backup, even on tiny hardware</h2>
|
||||||
|
<p>Despite the very limited RAM, this Pi still participates in the same
|
||||||
|
Prometheus monitoring pattern as the rest of the fleet — with the metrics
|
||||||
|
collector's default configuration trimmed down to only the essential
|
||||||
|
collectors, since the full default set noticeably overloaded the CPU on
|
||||||
|
this specific board. It also runs a nightly rsync backup of its own
|
||||||
|
configuration to the NAS over the internal network.</p>
|
||||||
|
|
||||||
|
<h2>Planned next steps</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Adding a USB webcam stream, mirroring the pattern already used for the 3D printer.</li>
|
||||||
|
<li>Migrating onto the planned isolated IoT network segment once that's fully set up.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
module.exports = function (eleventyConfig) {
|
||||||
|
eleventyConfig.addPassthroughCopy({ "shared/theme-smarthome.css": "theme.css" });
|
||||||
|
eleventyConfig.addPassthroughCopy({ "shared/base.css": "shared-base.css" });
|
||||||
|
eleventyConfig.addPassthroughCopy({ "shared/favicon/smarthome.svg": "favicon.svg" });
|
||||||
|
|
||||||
|
return {
|
||||||
|
dir: {
|
||||||
|
input: "smarthome",
|
||||||
|
includes: "../shared/_includes",
|
||||||
|
output: "dist/smarthome"
|
||||||
|
},
|
||||||
|
serverOptions: {
|
||||||
|
host: "0.0.0.0",
|
||||||
|
port: 8083
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
---
|
||||||
|
title: "Home Control Buttons"
|
||||||
|
section: "smarthome"
|
||||||
|
description: "Overview of the central buttons dashboard used to control lighting, smart plugs, music, network devices and monitoring shortcuts around the apartment."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<a class="back-link" href="/">← Back to infra overview</a>
|
||||||
|
<h1>Home Control Buttons</h1>
|
||||||
|
<div class="detail-content">
|
||||||
|
|
||||||
|
<p>Instead of jumping between multiple apps and web UIs, I built a single static HTML page with grouped buttons that trigger actions or link directly to the relevant tool for every physical device and service in the apartment. It's the fastest way to switch lights, kill music, check a sensor or jump into an admin dashboard from a phone or tablet without hunting through bookmarks.</p>
|
||||||
|
|
||||||
|
<h2>Lighting & ambience</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Balkon</strong> — LED on/off toggle for the balcony lighting.</li>
|
||||||
|
<li><strong>Beleuchtung (all rooms)</strong> — global "all on"/"all off" shortcut plus per-room toggles for living room and kitchen lighting.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Music controls</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Kitchen</strong> — quick buttons for SomaFM, a classical station, and an off switch, all routed to the kitchen speaker.</li>
|
||||||
|
<li><strong>Living room</strong> — the same SomaFM/classical options plus dedicated Lounge, 2000s and Deluxe stations, and an off switch.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Tasmota smart plugs</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>TasmoAdmin</strong> — link straight into the central Tasmota device dashboard for firmware and config management.</li>
|
||||||
|
<li><strong>Power strips (Gosund P1)</strong> — two multi-outlet strips covering the home-office network gear and backup drive, and the iMac corner with its peripherals.</li>
|
||||||
|
<li><strong>Single sockets (SP112)</strong> — four individually switchable outlets covering hallway, kitchen, dryer and washing machine circuits, each also powering an associated LED matrix or small Pi.</li>
|
||||||
|
<li><strong>Other plug families</strong> — additional Nous, Eightree (ESP32-based), Athom and IDS smart plugs cover spare capacity, living-room seating/desk outlets, storage room, kitchen appliances, and a few legacy TV/PC outlets (several currently marked defective).</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Special devices & status</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Tasmota RF Bridge / Delock</strong> — bridges RF-only devices (like the home-office 3D printer plug) into the Tasmota/MQTT ecosystem.</li>
|
||||||
|
<li><strong>LED Matrix restarts</strong> — one-click restart buttons for each room's LED matrix display, avoiding a manual power-cycle.</li>
|
||||||
|
<li><strong>Healthchecks & version info</strong> — direct links into the Healthchecks dashboard and a version/status overview for the connected devices.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Network shortcuts</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Routers & mesh</strong> — quick links to the main Fritzbox and its mesh repeaters covering different floors and the kitchen/IoT band.</li>
|
||||||
|
<li><strong>Switches</strong> — direct access to each Zyxel switch's admin page (server room, living room, PowerLAN, OpenWRT segment).</li>
|
||||||
|
<li><strong>OpenWRT & mini router</strong> — links into the OpenWRT admin UI and the small travel router used for testing.</li>
|
||||||
|
<li><strong>Sensors & MQTT</strong> — shortcuts to server-room and balcony sensor readings, bathroom analog values, and background worker/API status pages (HTML and JSON views).</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p class="redacted-note">This dashboard is intentionally simple: static HTML, no login, meant purely for convenience on the local network rather than as a secured control surface. Device names and room assignments are shown for structure; a few outlets are currently unused or marked defective and simply act as placeholders for future devices.</p>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
---
|
||||||
|
title: "HomematicIP + MQTT"
|
||||||
|
section: "smarthome"
|
||||||
|
parent: "/"
|
||||||
|
description: "How HomematicIP room sensors are bridged into MQTT and stored in InfluxDB for dashboards."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<h1>HomematicIP + MQTT</h1>
|
||||||
|
<div class="detail-content">
|
||||||
|
|
||||||
|
<p>HomematicIP room thermostats (living room, bedroom, home office) report
|
||||||
|
temperature and humidity into the smart home dashboard via a small,
|
||||||
|
deliberately low-tech bridge: <strong>Home Assistant → MQTT → InfluxDB</strong>.</p>
|
||||||
|
|
||||||
|
<h2>Why this path, and not a direct integration</h2>
|
||||||
|
<p>A dedicated HomematicIP-to-MQTT exporter used to run as a standalone
|
||||||
|
Docker container, but the underlying Python library lost compatibility
|
||||||
|
with HomematicIP's cloud API and hasn't been maintained since 2022. Home
|
||||||
|
Assistant, on the other hand, ships an actively maintained HomematicIP
|
||||||
|
Cloud integration — so instead of chasing a broken exporter, the bridge
|
||||||
|
now runs as native Home Assistant automations that simply republish
|
||||||
|
sensor state changes to MQTT.</p>
|
||||||
|
|
||||||
|
<h2>How it works</h2>
|
||||||
|
<ol>
|
||||||
|
<li>Home Assistant automations trigger on thermostat state changes.</li>
|
||||||
|
<li>Each automation publishes the current temperature and humidity to a per-room MQTT topic.</li>
|
||||||
|
<li>A small .NET background service subscribes to those topics and writes the values into InfluxDB, tagged by room and sensor type.</li>
|
||||||
|
<li>Grafana reads from InfluxDB for the temperature/humidity dashboards.</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<h2>Why this is worth documenting</h2>
|
||||||
|
<ul>
|
||||||
|
<li>No extra Docker container or unmaintained library dependency.</li>
|
||||||
|
<li>Cloud-API changes are absorbed by Home Assistant's own maintainers, not by custom code.</li>
|
||||||
|
<li>The MQTT bridge is just configuration (automations), not a service that needs its own uptime monitoring.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
---
|
||||||
|
title: "Smarthome"
|
||||||
|
section: "smarthome"
|
||||||
|
description: "Smart home projects: Home Assistant, MQTT sensors, AirPlay audio, OctoPrint, and energy monitoring."
|
||||||
|
layout: base.njk
|
||||||
|
sections:
|
||||||
|
- heading: "Control & Automation"
|
||||||
|
cards:
|
||||||
|
- title: "Home dashboard"
|
||||||
|
summary: "Lighting, music, and Tasmota device control shown on the home dashboard."
|
||||||
|
href: "/home-dashboard-controls/"
|
||||||
|
- title: "Tasmota & energy monitoring"
|
||||||
|
summary: "Smart plugs and power monitoring across the flat, feeding daily usage stats."
|
||||||
|
href: "/tasmota-energy/"
|
||||||
|
- title: "HomematicIP + MQTT"
|
||||||
|
summary: "How HomematicIP room sensors are bridged into MQTT and stored in InfluxDB."
|
||||||
|
href: "/homematic-mqtt/"
|
||||||
|
- title: "Balkon Pi"
|
||||||
|
summary: "Balcony sensor, lighting, and audio automation unit."
|
||||||
|
href: "/balkonpi/"
|
||||||
|
- heading: "Dashboards & Hardware"
|
||||||
|
cards:
|
||||||
|
- title: "LED Matrix"
|
||||||
|
summary: "ESP32 LED matrix displays around the flat. Full write-up currently lives on the CV site."
|
||||||
|
href: "https://stefankoelle.de/ledmatrix/"
|
||||||
|
- title: "WT32-SC01 dashboard"
|
||||||
|
summary: "Touch dashboard on a compact ESP32-S3 display."
|
||||||
|
href: "https://github.com/skoelle/wt32sc01-dashboard"
|
||||||
|
- title: "M5Stack dashboard"
|
||||||
|
summary: "ESP32 dashboard showing weather, calendar, and MVG departures."
|
||||||
|
href: "https://github.com/skoelle/m5stack-dashboard"
|
||||||
|
- heading: "Personal services"
|
||||||
|
cards:
|
||||||
|
- title: "iCloud Contacts Sync"
|
||||||
|
summary: "Private iCloud contacts synced into the homelab, with birthday notifier and web UI."
|
||||||
|
href: "https://github.com/skoelle/icloud-contacts-sync"
|
||||||
|
- title: "MVG departures"
|
||||||
|
summary: "Compact Munich transit departure monitor for desktop and mobile."
|
||||||
|
href: "https://github.com/skoelle/mvg-departures"
|
||||||
|
- title: "Google Calendar sync"
|
||||||
|
summary: "Private calendar synced into the homelab with notifer and web interface."
|
||||||
|
href: "https://github.com/skoelle/calender_sync"
|
||||||
|
- title: "Focus App"
|
||||||
|
summary: "A clean, modern todo application with drag & drop reordering."
|
||||||
|
href: "https://github.com/skoelle/focusapp"
|
||||||
|
- heading: "Media"
|
||||||
|
cards:
|
||||||
|
- title: "AirPlay audio"
|
||||||
|
summary: "Multi-room AirPlay receivers with software volume boost."
|
||||||
|
href: "/airplay-audio/"
|
||||||
|
- title: "OctoPrint"
|
||||||
|
summary: "Remote 3D printer control and camera streaming."
|
||||||
|
href: "/octoprint/"
|
||||||
|
- title: "TubeArchivist"
|
||||||
|
summary: "Self-hosted YouTube archiving running on the Synology."
|
||||||
|
href: "/tubearchivist/"
|
||||||
|
---
|
||||||
|
{% include "card-grid.njk" %}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
title: "OctoPrint"
|
||||||
|
section: "smarthome"
|
||||||
|
parent: "/"
|
||||||
|
description: "Remote 3D printer control and camera streaming with OctoPrint on Raspberry Pi."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<h1>OctoPrint</h1>
|
||||||
|
<div class="detail-content">
|
||||||
|
|
||||||
|
<p>A Raspberry Pi 3B runs OctoPrint for remote 3D-printer control, plus a
|
||||||
|
live camera stream so print progress can be checked without walking over
|
||||||
|
to the printer.</p>
|
||||||
|
|
||||||
|
<h2>Current setup</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>OctoPrint</strong> runs as a Docker container — the main remote control interface for the printer.</li>
|
||||||
|
<li><strong>Camera streaming</strong> is handled by a lightweight camera-server project, exposing both an HLS stream and an MJPEG stream, the latter wired directly into OctoPrint's webcam panel.</li>
|
||||||
|
<li><strong>Container management</strong> (Portainer, cAdvisor, a node exporter) runs alongside, giving the same monitoring pattern as the rest of the fleet.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>History</h2>
|
||||||
|
<p>This Pi originally also had a small status display attached; the
|
||||||
|
display was later moved to the backup Pi (which benefits more from local,
|
||||||
|
no-network status output) once this Pi's camera stream took over as the
|
||||||
|
primary way to check on things remotely. The unit was also renamed at
|
||||||
|
that point to better reflect its now-singular focus on the printer.</p>
|
||||||
|
|
||||||
|
<h2>Notable OS lesson</h2>
|
||||||
|
<p>After moving this Pi to a newer OS release, boot configuration files
|
||||||
|
moved to a new path — editing the old path silently does nothing, which
|
||||||
|
is an easy trap when following older notes or tutorials for the same
|
||||||
|
hardware.</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
---
|
||||||
|
title: "Tasmota & Energy Monitoring"
|
||||||
|
section: "smarthome"
|
||||||
|
parent: "/"
|
||||||
|
description: "Smart plugs and power monitoring across the flat with daily usage statistics."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<h1>Tasmota & Energy Monitoring</h1>
|
||||||
|
<div class="detail-content">
|
||||||
|
|
||||||
|
<p>Power monitoring across the flat runs on a mix of Tasmota-flashed smart
|
||||||
|
plugs, tracking accumulated kWh per device and feeding daily usage
|
||||||
|
figures into the home dashboard.</p>
|
||||||
|
|
||||||
|
<h2>What's metered</h2>
|
||||||
|
<table>
|
||||||
|
<tr><th>Device type</th><th>Typical use</th><th>Rough daily usage</th></tr>
|
||||||
|
<tr><td>Fridge plug</td><td>Kitchen appliance monitoring</td><td>~0.8 kWh/day (very stable)</td></tr>
|
||||||
|
<tr><td>Dishwasher plug</td><td>Appliance monitoring</td><td>~1.2 kWh/day (varies with use)</td></tr>
|
||||||
|
<tr><td>Office/desk plug</td><td>Workstation + peripherals</td><td>~1.2-1.7 kWh/day</td></tr>
|
||||||
|
<tr><td>Entertainment plugs (TV, Apple TV)</td><td>Media devices</td><td>Very low, Apple TV especially so</td></tr>
|
||||||
|
<tr><td>LED matrix displays</td><td>Ambient info displays</td><td>~0.04 kWh/day each</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2>Multi-socket power strips</h2>
|
||||||
|
<p>Several rooms use multi-outlet smart power strips (each socket
|
||||||
|
individually switchable and metered) rather than single smart plugs — this
|
||||||
|
keeps things like "washing machine + dryer" or "office desk cluster" on
|
||||||
|
one strip while still tracking each socket's consumption separately.</p>
|
||||||
|
|
||||||
|
<h2>Flashing process</h2>
|
||||||
|
<p>New devices go through a standard conversion flow (from the
|
||||||
|
manufacturer's original cloud-dependent firmware to Tasmota) using a
|
||||||
|
well-documented community process. It occasionally fails on the first
|
||||||
|
attempt due to a transient Wi-Fi handshake issue — retrying resolves it
|
||||||
|
without any special handling.</p>
|
||||||
|
|
||||||
|
<h2>What this data is used for</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Spotting appliances with unexpectedly high standby draw.</li>
|
||||||
|
<li>Sanity-checking that "turned off" devices are actually drawing near-zero power.</li>
|
||||||
|
<li>Feeding the home dashboard's live power figures alongside the climate sensors.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
---
|
||||||
|
title: "TubeArchivist"
|
||||||
|
section: "smarthome"
|
||||||
|
parent: "/"
|
||||||
|
description: "Self-hosted YouTube archiving and media management running on the Synology NAS."
|
||||||
|
layout: base.njk
|
||||||
|
---
|
||||||
|
<h1>TubeArchivist</h1>
|
||||||
|
<div class="detail-content">
|
||||||
|
|
||||||
|
<p>TubeArchivist runs on the Synology NAS via Docker Compose, self-hosting
|
||||||
|
a searchable archive of downloaded YouTube content (Elasticsearch +
|
||||||
|
Redis + the TubeArchivist app itself).</p>
|
||||||
|
|
||||||
|
<h2>Why this needed real troubleshooting</h2>
|
||||||
|
<p>Running Elasticsearch on Synology's DSM kernel isn't fully
|
||||||
|
straightforward: newer Elasticsearch releases require a Linux kernel
|
||||||
|
feature (SECCOMP) that Synology's kernel doesn't compile in. The fix is
|
||||||
|
version-pinning Elasticsearch to the last release that still works
|
||||||
|
without it, rather than fighting the kernel.</p>
|
||||||
|
|
||||||
|
<h2>Other Synology-specific quirks</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Synology's Container Manager doesn't always auto-join containers to the same Docker network even when they're defined in one Compose file — an explicit named network avoids silent connectivity failures between the app, Elasticsearch, and Redis.</li>
|
||||||
|
<li>A version upgrade of the app itself required a very specific step-by-step path (skipping versions breaks the database migration) rather than jumping straight to the latest release.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>What's backed up</h2>
|
||||||
|
<table>
|
||||||
|
<tr><th>Data</th><th>Included in NAS backup?</th></tr>
|
||||||
|
<tr><td>Downloaded media files</td><td>Yes, via the NAS's own backup volumes</td></tr>
|
||||||
|
<tr><td>Elasticsearch metadata/index</td><td>Yes, via the NAS's own backup volumes</td></tr>
|
||||||
|
<tr><td>Redis cache</td><td>No — safe to delete/rebuild, only holds session data</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</div>
|
||||||
Reference in New Issue
Block a user