privatecloud builder c57606cd90
CI / build (push) Successful in 58s
Fix OTLP log export: BatchLogRecordProcessor takes an options object
sdk-logs 0.221 changed the constructor to {exporter, ...} instead of a
bare exporter argument; passing the exporter directly silently left
this._exporter undefined, so every log export failed inside
suppressed error handling and no logs ever reached the collector.
2026-07-25 12:30:06 +00:00

HN Reader

A lightweight, keyboard-driven Hacker News reader. Navigate like Superhuman/Linear: j/k to move, o/Enter to read, e to archive, s to star, numbers to switch feeds. Feed data is cached in localStorage for instant loads, then revalidated in the background.

Press ? in the app for the full shortcut list.

Stack

  • Frontend: Vite + React + TypeScript, shadcn/ui (Radix primitives, Tailwind v4, Nova preset)
  • Backend: Express (server/), polls the HN Firebase API every 5 minutes and caches stories in Postgres
  • Database: Postgres 16 (Docker), schema in scripts/init.sql
  • Observability: OpenTelemetry traces/metrics/logs shipped via OTLP/HTTP

Architecture

src/            React SPA (feeds, keyboard nav, comments dialog)
server/         Express API + HN sync job + OTel bootstrap
scripts/        DB schema, backup script
docker-compose.yml   Postgres container for this app
.gitea/workflows/    CI: lint, typecheck, build

The API caches HN's top/new/best/ask/show/job feeds (100 items each) in Postgres so the frontend never talks to Hacker News directly. Per-story read/starred/archived state lives in story_state and is returned inline with each feed response.

Local development

cp .env.example .env        # fill in POSTGRES_PASSWORD
docker compose up -d        # start Postgres
npm install
npm run dev:server          # API on :3012 (loaded from .env)
npm run dev                 # Vite dev server on :3012, proxies /api

Production

Runs as the app-54d6877c systemd unit: builds are not automated on deploy, so after pulling changes run npm run build then restart the service (sudo systemctl restart app-54d6877c). The compiled SPA is served as static files by the same Express process that serves /api, on port 3012.

Nightly Postgres backups run via the app-54d6877c-backup.timer systemd timer (scripts/backup.sh), keeping 7 days of pg_dump snapshots in ~/backups.

Design system

Built with shadcn/ui (components.json, Radix base, Nova preset). Components live under src/components/ui and are added with npx shadcn@latest add <component> — nothing here is hand-rolled, so swapping in a custom registry later (e.g. a privatecloud registry) is a matter of updating components.json's registries field and re-running add against it.

Observability

The API sends traces, metrics, and logs to an OTLP/HTTP collector at OTEL_EXPORTER_OTLP_ENDPOINT (defaults to http://localhost:4318), instrumenting HTTP, Express, and Postgres calls automatically via @opentelemetry/auto-instrumentations-node. View it in Grafana at localhost:3400.

S
Description
Keyboard-driven Hacker News reader (Superhuman/Linear-style)
Readme
144 KiB
Languages
TypeScript 69.3%
JavaScript 20.5%
CSS 8.5%
Shell 0.9%
HTML 0.8%