Prufy notes
Make it stick ยท 5 min

Run it yourself

Reading only goes so far. Everything in these notes can be made to happen on your machine.

Start it

Needs Bun, and Docker running.

bun install
bun run db:up
bun run db:migrate
bun run dev

The app is at localhost:5200, and these notes are at localhost:5200/notes. There's nothing to sign in to. The first domain you enter gives this browser a prufy_session cookie, and every claim after that belongs to it.

Fake DNS: try every failure

DNS_MODE=fixtures bun run dev

Enter <name>.prufy-demo.dev on the landing page and press I've added it. To see a transfer, verify verified.prufy-demo.dev, then do it again in a private window. A private window has no cookie, so it becomes a second account.

Fixture domains
NameWhat you'll see
verifiedVerified
lookalikeOne character looks alike
truncatedPart of the value is missing
quotesQuote marks in the value
whitespaceAn extra space
prefixOnly the code, without its prefix
differentAnother code
staleAn older code. Get a new code first
nothingNothing at the name
doubledDomain in the host field twice
wrong-nameSaved at the apex
no-underscoreSaved without the underscore
wrong-providerAdded where the nameservers don't point
cnameA CNAME is using the name
no-such-zoneDomain doesn't exist
propagatingWaiting on public DNS, verified on the 3rd check
disagreeWaiting on the provider, verified on the 3rd check
unreachableTry again later
servfailOne of the provider's servers is failing
lameNameservers are listed but don't work
parkedThe domain is parked at a parking service
typo-then-fixedA typo, verified on the 2nd check
record-removedVerified, at risk, restored on the 4th check
mail.subzoneA subdomain inside a parent zone
one-clickOne-click available
one-click-not-enabledSupported, not enabled

Time travel

Dev only. Moves the server's clock, so time-based states can be reached by hand.

curl -X POST localhost:5200/api/dev/clock -H 'content-type: application/json' -d '{"advanceMs":90000000}'
curl -X POST localhost:5200/api/dev/run-worker
To seeadvanceMs
A verified claim re-checked (25 hours)90000000
An at-risk re-check (6 hours)21600000
A code expire, or grace end (8 days)691200000

Turn on alerts

Both channels are off until their keys are set, and the alerts card on the claim page only offers what's on. With neither, the card doesn't render. Set these in apps/web/.env.local.

VariableTurns on
RESEND_API_KEYEmail: alerts, invites and address confirmation. EMAIL_FROM's domain has to be verified in Resend
VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEYPush notifications. Both are needed
bunx web-push generate-vapid-keys

Look at the globe without a browser

Renders the shader's scenes to PNG files in apps/web/.globe-frames, so a change to globe.wgsl can be looked at in seconds.

node apps/web/scripts/render-globe.mjs

Look at real DNS

bun run dns:probe example.com

Look inside the database

docker compose exec postgres psql -U prufy -d prufy
SELECT domain_ascii, status, version, next_check_at FROM domain_claims;
SELECT type, actor_kind, at FROM claim_events ORDER BY at DESC LIMIT 20;
SELECT resolver, qname, rcode, answers FROM observations LIMIT 20;
SELECT account_id, left(token_hash, 12) AS hash, last_seen_at, expires_at FROM sessions;
SELECT claim_id, invited_email, opened_at, expires_at FROM claim_shares;

Checks before committing

bun run typecheck
bun run test
bun run check
bun run build

bun run check skips these notes because they're gitignored, and Turbo can replay a cached result for the others. The publish script below checks them directly.

Two sites, one repo

The app
Vercel project: prufy
API, database and a schedule. Built by Vercel from the repo root, with .vercelignore leaving these notes out.
These notes
Vercel project: prufy-notes
Plain static files with no server. Built on your machine by the publish script.

Deploy the app

Vercel deploys from GitHub, so pushing is usually all it takes.

What a push doesPick one
Builds
Deploys to
Production, the prufy project on Vercel
Database
The production Neon database
Worker
Vercel Cron, every minute
Who can open it
Anyone with the link
A branch's first deployment always builds, because there's no earlier deployment to compare it with.

To deploy by hand, run this from the repo root, because the app needs the workspace packages. Migrations never run on deploy, so when the schema changes, apply them first to both the production and preview databases, using Neon's direct connection.

vercel deploy . --prod --yes --project prufy --scope personal-d39c1b49
DATABASE_URL="$DATABASE_URL_UNPOOLED" bun run db:migrate

Publish these notes

This is how the copy on your phone gets updated. The app's API and worker need a server and its globe needs a shader build, so the published site contains only the notes, with no git connection. The script stops if the notes import anything the static copy can't build.

1
Check
Lint and typecheck the notes. A failure stops here.
2
Copy
Copy the repo to a temp folder, so your checkout doesn't change.
3
Strip
Delete every route except /notes.
4
Export
Build plain HTML, CSS and JS with output: export.
5
Publish
Deploy that folder to the prufy-notes project.
6
Confirm
Load every page on the live site and expect a 200.
bash apps/web/app/notes/_deploy/deploy.sh