~ knotless Search/
↑↓ move↵ open⇧↵ all results

grain.social/grain

photography-focused photo sharing platform

grain.social
1 collaborator
0
2

Deploy grain-serve from main in place of the old server

Draft
Draft chadtmiller.com wants to merge into main from grain.social/grain:rust/cutover

What changes

main deploys grain-serve. The root Dockerfile is grain-serve's (with its pages, the grain CLI, grain-mcp and grain-import), moved from deploy/Dockerfile, and .dockerignore from deploy/Dockerfile.dockerignore, replacing the old server's. .knotless/workflows/deploy.yml runs deploy/deploy.sh build on the host while the commit's checks run, deploy/deploy.sh up once check, test, web and lexicons have passed, and deploy/deploy.sh drop when one fails or main has moved past the commit. Deploys go to the compose project /etc/grain-serve, with the index at /mnt/data/grain-serve/index.db; /etc/grain's grain service is no longer touched.

Production's settings are in deploy.yml's env:, since deploy.sh writes the host's /etc/grain-serve/.env from its environment, whole: GRAIN_HOST, GRAIN_DOMAIN=grain.social, GRAIN_CDN=https://cdn.grain.social, GRAIN_ADMIN_DIDS, the APNs key ID, team and topic, GRAIN_FCM_PROJECT_ID, and GRAIN_CARRY_COOKIES_UNTIL=2026-12-15T00:00:00Z (the old server's cookies last 30 days). GRAIN_PROXY_NETWORK and GRAIN_DATA keep deploy.sh's defaults. The secrets stay in the host's /etc/grain-serve/serve.env.

The checks. rust.yml becomes check.yml and harness.yml test.yml, replacing the Node ones. What the Node check.yml and test.yml ran (npm run check, which is vp check and svelte-check over web/, then npm test) moves to a new web.yml, so the pages stay checked, and deploys wait on it: the pages ship in the same image. publish-lexicons.yml waits on the same four.

Docs. deploy/README.md describes the deploy as it is now, with no "Until cutover" or "At cutover". AGENTS.md's production section covers /etc/grain-serve, just logs, querying index.db with the image's node:sqlite, and the tables grain-serve's index cannot rebuild (oauth_key, web_session, carried_cookie_key, push_token, preference, notification_pref, notification_seen, label, mute, report, story_view, space_support, space_invite, takedown).

It also merges main's three commits since feature/rust left it: hatk alpha.115, photo writes always sending alt, and app-version.json. Git placed that file in web/static/, where feature/rust already has the same content.

Why

This is the repository's part of moving grain.social from the hatk appview to grain-serve on the Hetzner host. It cuts from feature/rust and brings that whole branch onto main.

Merging this is a step of the cutover script in hetzner-infra. Do not merge it before the host has switched: before Caddy proxies grain.social to grain-serve, the old server's database has been imported into /mnt/data/grain-serve/index.db, and the push key files are under /mnt/data/grain-serve/certs. The first push to main after the merge deploys grain-serve over whatever /etc/grain-serve holds.

How it was checked

  • The pull's checks on the runner (check, test, web, lexicons, e2e).
  • bash -n deploy/deploy.sh, and each workflow parsed as YAML.
  • deploy.yml runs only on a push to main, so this pull does not exercise it. Its first run is the deploy at cutover.

Shortcomings

  • The old server's code (server/, hatk.config.ts, the hatk dependency) is left in place for the rollback window. web.yml still runs its tests. Removing it is a follow-up pull.
  • The build step has 30 minutes, the runner's per step. A cold build of the Rust workspace on the host could need more. The cutover script should run deploy/deploy.sh by hand once first, which fills the cargo cache mounts this build reads.
  • hetzner-infra's nightly grain-backup.sh dumps the old server's /mnt/data/grain/grain.db. Pointing it at grain-serve's tables is hetzner-infra's change.

Review

Start with .knotless/workflows/deploy.yml, then the env: against deploy/deploy.sh's header.

🤖 Generated with Claude Code

chadtmiller.com added 250 commits