Bootstraps Vellum as a single Go binary with one SQLite database. - core: env-driven config, slog logging, SQLite (WAL) with pooled pragmas, embedded migration runner keyed on PRAGMA user_version - migrations/0001_core.sql: identity, media, user state, infra. Media items are global; all per-user state (progress, favorites, grants) is a join. One polymorphic progress table covers every media kind. - httpapi: request-ID, structured request logging, panic recovery, JSON endpoints for /healthz and /api/v1/version - multi-arch Dockerfile (pure-Go SQLite, CGO_ENABLED=0) with an entrypoint that honours PUID/PGID like the rest of the self-hosted stack - tests for migration idempotency, FK enforcement, WAL, config, middleware Media does not exist yet. Nothing is exposed beyond health and version.
107 lines
3.4 KiB
Markdown
107 lines
3.4 KiB
Markdown
# Vellum
|
|
|
|
One application, one process, one database for every media type — ebooks,
|
|
comics, audiobooks, music, photos, games, and video — behind a single account
|
|
system with real per-user progress and per-library permissions.
|
|
|
|
A modular monolith. One Go binary, one SQLite file, one web frontend.
|
|
|
|
The full plan lives in `~/.opencode/plan/vellum.md`.
|
|
|
|
## Status
|
|
|
|
| Phase | Scope | State |
|
|
| --- | --- | --- |
|
|
| **0** | Repo, config, migrations, health, Docker | **done** |
|
|
| 1 | Users, sessions, library grants | not started |
|
|
| 2 | Scanner, items, assets, FTS5 search | not started |
|
|
| 3 | EPUB reader + progress | not started |
|
|
| 4 | Comic (CBZ/PDF) viewers | not started |
|
|
| 5 | Metadata providers | not started |
|
|
| 6 | Audiobooks, music, photos | not started |
|
|
| 7 | Games | not started |
|
|
| — | Video (stays on Jellyfin for now) | deferred |
|
|
|
|
Phase 0 delivers a binary that boots, migrates its schema, serves a health
|
|
check, and shuts down cleanly. No media features yet.
|
|
|
|
## Quick start
|
|
|
|
```bash
|
|
sudo dnf install golang # once; Go is not in the base image here
|
|
make build
|
|
make run
|
|
curl localhost:7788/healthz # {"database":"ok","status":"ok",...}
|
|
```
|
|
|
|
Run with media roots configured:
|
|
|
|
```bash
|
|
VELLUM_MEDIA_DIRS=/mnt/storage/media/ebooks:/mnt/storage/media/comics make run
|
|
```
|
|
|
|
## Configuration
|
|
|
|
All configuration is environment variables. Every one has a default, so the
|
|
binary runs with no configuration at all.
|
|
|
|
| Variable | Default | Meaning |
|
|
| --- | --- | --- |
|
|
| `VELLUM_LISTEN` | `:7788` | Bind address |
|
|
| `VELLUM_DATA_DIR` | `./data` | SQLite database lives here |
|
|
| `VELLUM_CONFIG_DIR` | `./config` | Regenerable cache (thumbnails, pages) |
|
|
| `VELLUM_MEDIA_DIRS` | *(none)* | Read-only library roots, colon-separated |
|
|
| `VELLUM_LOG_LEVEL` | `info` | `debug`, `info`, `warn`, `error` |
|
|
| `VELLUM_LOG_FORMAT` | `text` | `text` or `json` |
|
|
| `VELLUM_SESSION_TTL` | `720h` | Login session lifetime |
|
|
| `VELLUM_SHUTDOWN_GRACE` | `15s` | Graceful shutdown timeout |
|
|
|
|
## Layout
|
|
|
|
```
|
|
cmd/vellum/ main — wiring and lifecycle only
|
|
internal/
|
|
core/ config, logging, database, migration runner
|
|
httpapi/ router, middleware, handlers
|
|
migrations/ embedded SQL, applied in order at boot
|
|
```
|
|
|
|
**Module rule:** the core never imports a feature module; modules import the
|
|
core and mount their own routes, tables, and scanner rules. Adding a media type
|
|
is a new package plus a migration, never a core edit.
|
|
|
|
**Schema rule:** media items are global; everything user-specific is a join.
|
|
Nothing may hold per-user state on a media row.
|
|
|
|
## Database
|
|
|
|
SQLite in WAL mode, opened through `modernc.org/sqlite` — a pure-Go driver, so
|
|
the binary builds with `CGO_ENABLED=0` and cross-compiles without a toolchain
|
|
per target.
|
|
|
|
Migrations are embedded and applied in filename order; applied files are
|
|
recorded only by number in `PRAGMA user_version`, so **never edit an applied
|
|
migration** — add a new one.
|
|
|
|
Back up by copying the database (or its directory); `VACUUM INTO 'backup.db'`
|
|
takes a consistent snapshot while running.
|
|
|
|
## Deploy
|
|
|
|
```bash
|
|
make docker # podman build
|
|
podman compose up -d # see compose.yml
|
|
```
|
|
|
|
The container mounts `/data` (database), `/config` (cache), and `/media`
|
|
(read-only). On SELinux hosts every bind mount needs `:z` — `compose.yml`
|
|
already has it.
|
|
|
|
The application never writes to `/media`.
|
|
|
|
## Development
|
|
|
|
```bash
|
|
make check # gofmt + vet + test
|
|
make test
|
|
```
|