Skip to content

HanifCarroll/read-later-lab

Repository files navigation

Read Later Lab

A weekend architecture experiment: a production-shaped Go API server with Postgres, sqlc, Kamal deployment, and an embedded SvelteKit frontend for a small AI-assisted read-it-later app.

The app lets you save links to articles and blog posts, capture why you saved them, and ask OpenAI to generate a brief triage card with a summary, estimated read time, recommended reading mode, best sections to read, and read/skip guidance.

Features

  • Save article/blog post URLs with a personal “why I saved this” note.
  • Fetch article metadata and page text from the Go backend.
  • Store items in Postgres using sqlc-generated queries.
  • Run embedded migrations on app startup.
  • Generate AI triage output with OpenAI:
    • brief summary
    • estimated read time
    • recommended mode: read fully, skim, reference, or skip
    • best sections to read
    • “You should read this if” bullets
    • “You should skip this if” bullets
  • Triage saved items as read soon, skim later, reference, skipped, or archived.
  • Run SvelteKit with Vite HMR in development.
  • Build SvelteKit to static files and embed them into a single Go binary for production.
  • Deploy with Docker + Kamal + a Postgres accessory.

Architecture

cmd/server                  Go HTTP server entrypoint
internal/app                domain model, article fetching, OpenAI analysis
internal/db                 sqlc schema, queries, generated Go code
internal/storage            Postgres store + embedded migrations
internal/httpserver         API routes and SvelteKit asset/proxy serving
internal/storage/migrations Postgres migrations embedded into the binary
ui/svelte                   SvelteKit frontend
web/build                   generated static frontend assets embedded by Go
config/deploy.yml           Kamal deployment config
compose.yml                 local/container Postgres + app stack

Routes:

  • /app — SvelteKit frontend.
  • /api/items — JSON API consumed by SvelteKit.
  • /healthz — deployment health check.
  • / — redirects to /app.

Local setup

Create a local .env file and add your OpenAI key:

cp .env.example .env

Then edit .env:

OPENAI_API_KEY=sk-your-key-here
OPENAI_MODEL=gpt-5.4-mini
ADDR=:8080
DATABASE_URL=postgres://read_later:password@127.0.0.1:55432/read_later?sslmode=disable
POSTGRES_PASSWORD=password

.env is ignored by git.

Development

make dev

Open http://localhost:8080.

Dev mode starts:

  • Postgres via Docker Compose on localhost:5432
  • Go server on localhost:8080
  • SvelteKit/Vite dev server on localhost:5173
  • Go proxying /app/* to Vite for HMR

Container stack

make compose-up

This builds the production container and starts Postgres locally.

Build

make build
./server

The production binary serves the embedded SvelteKit build from web/build and runs DB migrations on startup.

sqlc

After changing internal/db/schema.sql or internal/db/queries/*.sql, regenerate query code:

make sqlc

Kamal deployment

The Kamal config is in config/deploy.yml. It deploys:

  • one Go web container
  • one Postgres accessory
  • GHCR image: ghcr.io/hanifcarroll/read-later-lab

Required secrets:

KAMAL_REGISTRY_PASSWORD
POSTGRES_PASSWORD
DATABASE_URL
OPENAI_API_KEY

For production, DATABASE_URL should point at the Kamal accessory, for example:

postgres://read_later:<password>@read-later-lab-postgres:5432/read_later?sslmode=disable

Private Tailscale deployment

The Tailscale overlay in config/deploy.tailscale.yml disables the public Kamal proxy and publishes the app only on VPS localhost:

kamal deploy -d tailscale

Then expose that private localhost port to your tailnet from the VPS:

tailscale serve --bg 8080

Access it from any device in your tailnet at the HTTPS URL shown by:

tailscale serve status

The VPS hostname is configured as hetzner-vps when joining Tailscale.

RAM checks on the VPS

kamal app exec 'ps -o pid,rss,comm -C server'
kamal app exec 'cat /sys/fs/cgroup/memory.current'
kamal accessory exec postgres 'ps aux'
ssh root@<vps-host> 'docker stats --no-stream'

Verification

Useful checks:

make test
make build

About

AI-assisted read-it-later app experimenting with a Go API and embedded SvelteKit frontend

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors