Cloud|

Notes website

Run the standalone Notes server with a dedicated Postgres database and check its readiness.

4 min read Updated 2026-10-30 #operations#notes

The Notes website is a standalone service under pwas/notes. It uses a dedicated Postgres database. It does not connect to a Cloud installation.

The cloud-notes image contains the SPA, API, WebSocket and admin view. It runs as an unprivileged user on port 3000 and is built for each release architecture. For a source checkout, build the UI package and Notes SPA before starting:

bash
bun run --cwd packages/ui build
bun run --cwd pwas/notes build

Set DATABASE_URL to that database and start the server:

bash
bun run --cwd pwas/notes start

Startup applies the versioned migrations in a transaction. Give this database user permission to create and alter the Notes tables. Use a separate database from Cloud: the Notes service owns its schema and lifecycle.

On port 3000, /healthz returns 200 while the process is running. /readyz returns 200 only when the database is reachable and every schema migration matches this server. A failed readiness check returns 503; keep the service out of traffic until it recovers. SIGTERM and SIGINT stop the listener and close the database pool.

This service is being prepared for a later public launch. A working health check does not mean that public sharing or the launch requirements have been accepted.

Sharing runtime

Notes runs exactly one server instance. Startup acquires a Postgres advisory lock and installs an owner fence checked by every mutation. API writes and WebSocket upgrades require NOTES_PUBLIC_ORIGIN; set it to the external HTTPS origin. Only NOTES_TRUSTED_PROXY may supply the final X-Forwarded-For address. Otherwise the transport peer supplies the rate-limit key.

Set NOTES_STORAGE_BYTES, NOTES_MAX_CONNECTIONS, and NOTES_MAX_OPEN_PADS before startup. The latter two come from the pre-launch load test, rather than unmeasured production defaults. The storage budget counts application payloads and fixed row costs. Provision additional disk for indexes, WAL, backups, and compaction. New notes and uploads pause at 90%; existing updates may use the remaining capacity. /api/status exposes the pause/full states and /readyz returns 503 at capacity. Rate counters reset on process restart.

The browser retains unacknowledged updates and retries them after reconnect. The server acknowledges only committed updates, compacts covered log rows atomically, and drains its queues before shutdown. Six-month expiry is based on an explicit browser human-open signal. GET/HEAD, previews and idle sockets never extend retention. Interrupted deletion cleanup resumes on later sweeps.

Before exposing the service, arrange a daily Postgres dump retained for 14 days, verify a restore, and measure an RPO of at most 24 hours and an RTO of 4 hours. Backup retention can outlast deletion from the running database. Keep capability paths out of access, error, tracing, and telemetry logs at every proxy layer; the application emits only fixed diagnostic codes.

The Notes process serves the SPA, API and WebSocket together. Shared HTML carries escaped Open Graph metadata; both capabilities use the same read-link preview image. PNG rendering uses a bundled font and local numeric chart data without network requests. The in-memory image cache is bounded by the configured open note budget and expires when content changes. GET and HEAD never write durable data; HEAD does not invoke the rasterizer. Preserve the no-referrer, noindex and no-store response headers for note data at the proxy, and disable path logging for this host. The service worker revalidates (no-cache); hashed static build assets use a one-year immutable cache. The read-only POST /api/presence polls existing editor awareness without opening notes, writing data, or extending retention; it accepts 200 read links and 50 requests per minute per IP.

Moderation duty

Administration is disabled until NOTES_ADMIN_PASSWORD_HASH contains an Argon2id password hash. Without it, /admin and its APIs return 404. Serve /admin through the same HTTPS origin as the SPA. Sessions expire after 12 hours; changing the hash invalidates them all. Every mutation checks Origin and CSRF, including login. Never cache admin responses.

Assign a moderator and deputy before public launch. Reports receive an intake number. The admin view records the decision and reason, removes blocked/deleted content, and shows pending reporter notifications. Send the prepared decision email in the operator's mail application, then confirm it in the admin view. This explicit delivery step needs no additional Notes mail configuration. Storage and automatic-maintenance failures remain visible in the admin view and fixed-code application logs. Reserve Postgres disk outside the application budget for WAL, indexes, backups and transaction/compaction headroom.

Connect a coding agent

The Cloud skill provides compact working instructions. MCP supplies exact current documentation when details matter.

CLI command
bunx skills add https://docs.example.com

This command installs the working instructions published by this website in the selected coding agent.

For exact, current details, also connect the MCP server through one of the agent tabs.

Installation uses the open-source Vercel Skills CLI.

The skill and MCP complement each other: the skill describes workflows, while MCP supplies current documentation.