Blog can now pull live gaming data and surface a curated gamer profile. - DB: integrations config table (single JSON row, holds secrets, excluded from export), integration_cache table (lazy TTL cache), posts.links column - Platform fetchers: Steam (official Web API), PSN (NPSSO->token->trophies), Xbox (OpenXBL). All degrade gracefully; cache keeps last-good payload on fetch failure so pages never blank - Admin /integrations: profile/bio, social links, consoles, favorites, per-platform creds (blank keeps stored secret), cache TTL, live status table with refresh - Public /bio page: avatar, bio, recent games, achievements, favorites, consoles. Gated behind hasIntegrations() so a vanilla blog stays clean - About page bio teaser, Shell "now playing" tray widget + Bio nav link - Per-post "shared on" social links (editable in PostForm, shown on post page) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
🕹️ RetroBlog
A self-hosted blog engine with swappable OS / game-console skins.
Write Markdown posts, flip between retro themes (Windows, PS1, PS2, PS3, Wii, NDS, Dreamcast, JV2002…), and run the whole thing from a single SQLite file.
Features
- Markdown posts — create / edit / delete with slugs, tags, excerpts, dates.
- Swappable skins — pick the default theme; optionally let visitors switch.
- Admin panel — single-password login, signed session cookie.
- Import / export — JSON backup of settings and posts.
- Zero external services — data lives in one SQLite file (
data/blog.db).
Stack: Next.js 16 (App Router) · React 19 · better-sqlite3 · marked.
Quick start (development)
pnpm install
pnpm dev
Open http://localhost:3000. Admin panel at http://localhost:3000/admin
(default password admin in dev).
Configuration
All config is environment variables. Copy .env.example to .env and edit.
| Variable | Required | Default | Purpose |
|---|---|---|---|
ADMIN_PASSWORD |
prod | admin |
Password for /admin. Set this in production. |
ADMIN_SESSION_SECRET |
prod | ADMIN_PASSWORD |
Signs the admin session cookie. Use a long random string. |
PORT |
no | 3000 |
Port the server listens on. |
Generate a secret:
openssl rand -hex 32
Data is stored in ./data/blog.db (created automatically). Back up that
directory to back up the whole site.
Deploy — Docker (recommended)
Needs Docker + the Compose plugin.
# 1. Set production secrets
cp .env.example .env
$EDITOR .env # set ADMIN_PASSWORD and ADMIN_SESSION_SECRET
# 2. Build and run
docker compose up -d --build
Site is live on http://localhost:3000. The database persists in the
retroblog-data Docker volume across rebuilds.
Common operations:
docker compose logs -f # tail logs
docker compose down # stop (keeps the data volume)
docker compose up -d --build # update after pulling new code
Back up the database:
docker compose exec retroblog sh -c 'cat /app/data/blog.db' > backup-$(date +%F).db
Put a TLS-terminating reverse proxy (Caddy, nginx, Traefik) in front for HTTPS in production. RetroBlog speaks plain HTTP on
PORT.
Deploy — bare metal
Needs Node.js 22+ and pnpm (corepack enable). A C toolchain
(build-essential, python3) is required once, to compile better-sqlite3.
# 1. Install deps and build
pnpm install --frozen-lockfile
pnpm build
# 2. Set production secrets
export ADMIN_PASSWORD='your-strong-password'
export ADMIN_SESSION_SECRET="$(openssl rand -hex 32)"
# 3. Start
pnpm start # serves on PORT (default 3000)
Run it as a service (systemd)
/etc/systemd/system/retroblog.service:
[Unit]
Description=RetroBlog
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/retroblog
ExecStart=/usr/bin/pnpm start
Restart=on-failure
Environment=NODE_ENV=production
Environment=PORT=3000
Environment=ADMIN_PASSWORD=your-strong-password
Environment=ADMIN_SESSION_SECRET=your-long-random-secret
User=www-data
Group=www-data
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now retroblog
The SQLite database lives in WorkingDirectory/data/blog.db — make sure the
service User can write there, and back up that file.
Admin panel
Sign in at /admin with ADMIN_PASSWORD. From there:
- Posts — full create / edit / delete with Markdown bodies, slugs, tags, dates.
- Settings — branding (title, subtitle, footer, version), default theme for new visitors, whether the public theme switcher is shown, and which skins it offers.
- Import / export — download a JSON backup of settings and/or posts, and import one back (posts can replace or append).
All /admin/* routes are gated by middleware behind the signed session cookie.
License
See repository.