En selvhostet webapp for å samle og dele favoritter — filmer, sanger, biler, lego, blomster, bilder, hva som helst. Én Go-binary, SQLite, HTMX. Enkel å drifte.
  • Go 82.2%
  • HTML 12.7%
  • JavaScript 2.5%
  • CSS 1.4%
  • Makefile 0.7%
  • Other 0.5%
Find a file
Ole-Morten Duesund fe925fc292 feat: add OG meta tags to home page, tag browse, and profile bio
Complete Open Graph coverage across all public pages:

- Home page: og:title (site name), og:description, og:type=website,
  og:url, og:site_name
- Tag browse: og:title with tag name, og:description with count,
  og:url, og:site_name
- Profile: add og:description using bio (truncated to 200 chars)
  with fallback to generic text

Previously only fave detail and profile (without description) had
OG tags.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 10:54:23 +02:00
cmd/favoritter feat: add JSON REST API under /api/v1/ 2026-03-29 16:13:23 +02:00
dist fix: remove chatty install message from postinstall script 2026-03-29 19:28:22 +02:00
internal refactor: simplify PWA handlers and fix review findings 2026-04-07 10:47:13 +02:00
web feat: add OG meta tags to home page, tag browse, and profile bio 2026-04-07 10:54:23 +02:00
.dockerignore fix: address code review findings for Phase 7-8 2026-03-29 16:39:10 +02:00
.gitignore feat: add packaging, deployment, error pages, and project docs 2026-03-29 16:34:32 +02:00
Caddyfile.example feat: add packaging, deployment, error pages, and project docs 2026-03-29 16:34:32 +02:00
CHANGELOG.md release: v0.1.0 2026-03-29 19:05:28 +02:00
CLAUDE.md feat: add packaging, deployment, error pages, and project docs 2026-03-29 16:34:32 +02:00
compose.yaml feat: add packaging, deployment, error pages, and project docs 2026-03-29 16:34:32 +02:00
Containerfile fix: update Containerfile to Go 1.26 matching go.mod 2026-03-29 19:05:12 +02:00
go.mod feat: add Atom feeds and JSON/CSV import/export 2026-03-29 16:11:44 +02:00
go.sum feat: add Atom feeds and JSON/CSV import/export 2026-03-29 16:11:44 +02:00
LICENSE feat: implement Phase 1 (auth) and Phase 2 (faves CRUD) foundation 2026-03-29 15:55:22 +02:00
Makefile fix: use envsubst for nfpm variable expansion in Makefile 2026-03-29 19:18:14 +02:00
nfpm.yaml fix: address code review findings for Phase 7-8 2026-03-29 16:39:10 +02:00
PLANS-v1.1.md test: add comprehensive test suite (44 → 169 tests) and v1.1 plan 2026-04-04 00:18:01 +02:00
PLANS.md docs: add PLANS.md with roadmap for v1.1, v1.2, and future 2026-03-29 19:33:24 +02:00
README.md docs: add README with features, config, deployment, and API docs 2026-03-29 16:20:48 +02:00
TESTPLAN.md test: add comprehensive test suite (44 → 169 tests) and v1.1 plan 2026-04-04 00:18:01 +02:00

Favoritter

A self-hosted web application for collecting and sharing favorites. Movies, songs, cars, lego sets, flowers, pictures — anything you love.

Built as a single Go binary with SQLite, server-rendered HTML, and HTMX for interactivity. Designed to be easy to deploy for technical users who can share access with friends and family.

Features

  • Favorites — Add favorites with a description, optional URL, optional image upload, and tags
  • Tags — Autocomplete from existing tags, browse by tag
  • Privacy — Each favorite can be public or private, with a configurable default
  • User profiles — Public or limited visibility, with avatar, bio, and display name
  • Admin panel — User management, tag management, signup request approval, site settings
  • Signup modes — Open registration, approval-required, or closed
  • Atom feeds — Global, per-user, and per-tag feeds
  • Import/Export — JSON and CSV, for data portability
  • JSON API — Full REST API under /api/v1/ for third-party clients
  • OpenGraph — Rich link previews when sharing favorites
  • Accessible — Semantic HTML, keyboard navigation, screen reader support, lang="nb", WCAG 2.2 AA target
  • Proxy-aware — Supports deployment behind Caddy/nginx on a different machine (WireGuard, Tailscale)

Quick Start

Run directly

# Build
go build -o favoritter ./cmd/favoritter

# Run (creates admin user on first start)
FAVORITTER_ADMIN_USERNAME=admin \
FAVORITTER_ADMIN_PASSWORD=changeme \
./favoritter

Open http://localhost:8080 in your browser.

Run with Podman/Docker

BUILDAH_FORMAT=docker podman build \
  --build-arg BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
  --build-arg GIT_REVISION="$(git describe --always --dirty)" \
  -t favoritter .

podman run -d \
  -p 8080:8080 \
  -v favoritter-data:/data \
  -e FAVORITTER_ADMIN_USERNAME=admin \
  -e FAVORITTER_ADMIN_PASSWORD=changeme \
  favoritter

Configuration

All configuration is via environment variables. Sensible defaults are provided for everything except the initial admin credentials.

Variable Default Description
FAVORITTER_ADMIN_USERNAME (none) Initial admin username (created on first run)
FAVORITTER_ADMIN_PASSWORD (none) Initial admin password
FAVORITTER_DB_PATH ./data/favoritter.db SQLite database file path
FAVORITTER_LISTEN :8080 Listen address (e.g. :8080, 10.0.0.5:8080)
FAVORITTER_BASE_PATH / Base path for subpath deployment (e.g. /faves)
FAVORITTER_EXTERNAL_URL (auto) Public URL (e.g. https://faves.example.com). Used for feeds, cookies, OpenGraph. If unset, inferred from Host header.
FAVORITTER_TRUSTED_PROXIES 127.0.0.1 Comma-separated IPs/CIDRs to trust for X-Forwarded-For (e.g. 100.64.0.0/10 for Tailscale)
FAVORITTER_UPLOAD_DIR ./data/uploads Directory for uploaded images
FAVORITTER_MAX_UPLOAD_SIZE 10485760 Maximum upload size in bytes (10 MB)
FAVORITTER_SESSION_LIFETIME 720h Session cookie lifetime (30 days)
FAVORITTER_ARGON2_MEMORY 65536 Argon2id memory cost in KiB (64 MB)
FAVORITTER_ARGON2_TIME 3 Argon2id iterations
FAVORITTER_ARGON2_PARALLELISM 2 Argon2id parallelism
FAVORITTER_RATE_LIMIT 60 Auth endpoint rate limit (requests/minute/IP)
FAVORITTER_SITE_NAME Favoritter Site name shown in UI and feeds
FAVORITTER_DEV_MODE false Enable live template reload and debug logging

Deployment

Behind Caddy (subdomain)

faves.example.com {
    reverse_proxy 10.0.0.5:8080
}

Set FAVORITTER_EXTERNAL_URL=https://faves.example.com and FAVORITTER_TRUSTED_PROXIES=<caddy-ip>.

Behind Caddy (subpath)

example.com {
    handle_path /faves/* {
        reverse_proxy 10.0.0.5:8080
    }
}

Set FAVORITTER_BASE_PATH=/faves and FAVORITTER_EXTERNAL_URL=https://example.com/faves.

Remote proxy (WireGuard/Tailscale)

Caddy and Favoritter can run on different machines. Set FAVORITTER_TRUSTED_PROXIES to the proxy's IP or CIDR (e.g. 100.64.0.0/10 for Tailscale, 10.0.0.0/24 for WireGuard).

API

The JSON API lives under /api/v1/. Authenticate by posting to /api/v1/auth/login to get a session cookie, then use it for subsequent requests.

# Login
curl -c cookies.txt -X POST http://localhost:8080/api/v1/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"username":"admin","password":"changeme"}'

# List your faves
curl -b cookies.txt http://localhost:8080/api/v1/faves

# Create a fave
curl -b cookies.txt -X POST http://localhost:8080/api/v1/faves \
  -H 'Content-Type: application/json' \
  -d '{"description":"Blade Runner 2049","url":"https://example.com","privacy":"public","tags":["film","sci-fi"]}'

# Export
curl -b cookies.txt http://localhost:8080/api/v1/export/json

See the route list for all available endpoints.

Routes

Web

Method Path Description
GET / Home page / public feed
GET/POST /login Login
GET/POST /signup Signup (mode-dependent)
POST /logout Logout
GET/POST /reset-password Forced password reset
GET /faves Own faves list
GET /faves/new New fave form
POST /faves Create fave
GET /faves/{id} View fave
GET /faves/{id}/edit Edit fave form
POST /faves/{id} Update fave
DELETE /faves/{id} Delete fave
GET /tags/search?q= Tag autocomplete (HTMX)
GET /tags/{name} Browse by tag
GET /u/{username} Public profile
GET/POST /settings User settings
POST /settings/avatar Upload avatar
POST /settings/password Change password
GET /export Export page
GET /export/json Download JSON
GET /export/csv Download CSV
GET/POST /import Import page
GET /feed.xml Global Atom feed
GET /u/{username}/feed.xml User Atom feed
GET /tags/{name}/feed.xml Tag Atom feed
GET /admin Admin dashboard
GET/POST /admin/users User management
GET /admin/tags Tag management
GET/POST /admin/signup-requests Signup request management
GET/POST /admin/settings Site settings
GET /health Health check

API (/api/v1/)

Method Path Description
POST /api/v1/auth/login Login, returns session
POST /api/v1/auth/logout Logout
GET /api/v1/faves List own faves
POST /api/v1/faves Create fave
GET /api/v1/faves/{id} Get fave
PUT /api/v1/faves/{id} Update fave
DELETE /api/v1/faves/{id} Delete fave
GET /api/v1/tags?q= Search tags
GET /api/v1/users/{username} Public profile
GET /api/v1/users/{username}/faves User's public faves
GET /api/v1/export/json Export own faves
POST /api/v1/import Import faves

Tech Stack

  • Go (1.22+ stdlib router, html/template, embed, log/slog)
  • SQLite via modernc.org/sqlite (pure Go, no CGO)
  • HTMX for interactive UI without a JS framework
  • Pico CSS for semantic HTML styling
  • Argon2id for password hashing
  • gorilla/feeds for Atom feed generation

Only 3 external Go dependencies.

Security

  • Argon2id password hashing with timing-attack prevention
  • CSRF double-submit cookie pattern (auto-included by HTMX)
  • Security headers: CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy
  • Rate limiting on authentication endpoints
  • Image uploads: EXIF metadata stripped, re-encoded, UUID filenames, path traversal protection
  • Proxy-aware: trusts X-Forwarded-For only from configured proxy CIDRs
  • Session cookies: HttpOnly, Secure (from EXTERNAL_URL or X-Forwarded-Proto), SameSite=Lax

Development

# Run in dev mode (live template reload, debug logging)
FAVORITTER_DEV_MODE=true \
FAVORITTER_ADMIN_USERNAME=admin \
FAVORITTER_ADMIN_PASSWORD=dev \
go run ./cmd/favoritter

# Run tests
go test ./...

License

GNU Affero General Public License v3.0 (AGPL-3.0-or-later)