Onboarding to a tranqui-pds
TypeScript 55%
JavaScript 34%
HTML 8%
Shell 2%
Python <1%
<1%

README.md

haiku.garden #

Tooling for the haiku.garden tranquil-pds instance — the Naviar Haiku community's self-hosted ATProto PDS.

onboard.js + oauth.js #

Onboarding is two separate steps now (untangled 2026-07-23 — see ONBOARDING.md's History section for why). onboard.js create creates the member's account with their own real password, sends tranquil-pds's native verification email, and prints a "finish profile" link — nothing on our side babysits verification. Whenever the member wants (after verifying), that link kicks off oauth.js's real ATProto OAuth flow (public client, @atproto/oauth-client-node, scoped to just the two blento collections); the callback writes a themed starter blento.app profile (real app.blento.page + app.blento.node records) directly through the OAuth session, idempotently, then redirects to the member's journey page (/journey, content in journey.json) — plyr.fm, this week's Naviar Haiku challenge, chat, events — rather than leaving them on a populated profile with nothing else to do next. See ONBOARDING.md's "The journey page" section. workflow.js's OnboardingWorkflow (on Restate) tracks the outcome for observability — it can't execute the write itself, since an OAuth session's DPoP key material isn't JSON-serializable into a workflow input.

node onboard.js create --theme musician --handle name.haiku.garden \
  --email member@example.com --password "..." --display-name "Name" \
  --bio "..." --invite-code haiku-garden-xxxxx-xxxxx

Deployed live as three systemd services on the VPS (Node 22 via nvm — the dependency tree needs >=20, see ONBOARDING.md's Node-version note): haiku-restate-server, haiku-onboard-workflow, haiku-onboard-form.

server.js #

Self-serve alternative to running create by hand for every signup — a small public-facing form (own node:http server, no framework) where a member enters their own handle/email/password/bio/invite-code, plus the "finish profile" OAuth callback route from oauth.js. Calls the same createSignup() the CLI uses. See ONBOARDING.md's "Self-serve form" section for what it deliberately doesn't do yet (rate-limiting, invite-code management UI) and the invite-code enforcement caveat.

npm run serve   # PORT and PDS_URL env vars, defaults to :8787 / https://haiku.garden

themes/ #

Starter profile skeletons per new-member type:

  • musician.json — Bluesky presence + plyr.fm + Bandcamp embeds
  • haiku-poet.json — Bluesky presence + a featured haiku
  • general.json — minimal Bluesky presence + bio, for anyone else

See themes/SCHEMA.md for the exact record shapes and {{placeholder}} variables, verified directly against flo-bit/blento source — including the v2 node-graph schema (app.blento.node/app.blento.page), not the older app.blento.card/site.standard.publication shape blento.app's frontend no longer reads.

blento's schema is still moving. flo-bit is building a more flexible app.blento.theme (visual design tokens) model on top of the current node-graph — confirmed (2026-07-03) as not yet stable. onboard.js targets what's actually live today; revisit once he ships docs.

scripts/create-invite-code.mjs #

Mints a real, single-use tranquil-pds invite code via the admin account (torsten.haiku.garden) — the two calls (createSession, then createInviteCode) the 2026-07-07 live test did by hand. No local state; prints the code and exits.

~/txt/tracker/.keys/use haiku.garden-admin/account-password -- node scripts/create-invite-code.mjs

Reads the admin password from $SECRET via .keys/use (see .keys/README.md) — never a plaintext file path or a CLI flag.

scripts/leaflet-publish.js #

Publishes a markdown draft as a pub.leaflet.document under the haiku.garden devlog publication on leaflet.pub — a direct ATProto write (com.atproto.repo.createRecord), not the leaflet.pub UI. Always emits plain-text blocks (no bold/link richtext facets, to avoid hand-computing UTF-8 byte offsets) — correctness over polish, the same choice made for devlogs #0/#1 and the GEB dialogue piece this replaces the one-off version of.

node scripts/leaflet-publish.js --draft path/to/draft.md --title "Devlog #2" --dry
~/txt/tracker/.keys/use haiku.garden-bsky/app-password -- node scripts/leaflet-publish.js --draft path/to/draft.md --title "Devlog #2"
~/txt/tracker/.keys/use haiku.garden-bsky/app-password -- node scripts/leaflet-publish.js --draft path/to/dialogue.md --title "..." --mode dialogue

--mode paragraphs (default) splits on blank lines; --mode dialogue turns **SPEAKER:** text lines into SPEAKER: text blocks. Always --dry first (no credential needed) and hand-trim the draft of any meta-notes (draft headers, cross-references to unpublished pieces) that shouldn't go out verbatim. Reads the leaflet-devlog app-password from $SECRET via .keys/use — never a plaintext file path.

scripts/spaceship-dns.sh #

Add/list DNS records for haiku.garden via the Spaceship API, instead of hand-rolling a curl call each time. Upserts by (name, type) — never replaces the whole recordset, so it's safe to add one record without reviewing every other one first (though list before add if unsure).

scripts/spaceship-dns.sh list
scripts/spaceship-dns.sh add CNAME devlog 731fb7077db63b1d.vercel-dns-016.com.

Reads the API-caddy-haiku-garden key from ~/txt/tracker/.keys/spaceship (same key used for the wildcard cert's DNS-01 challenge and the _atproto TXT record). Known API quirk baked in: CNAME records need a cname field, not the target field the official docs example shows.

scripts/chatto-testkit.sh #

Scratch-environment machinery for testing against chatto.tilde.style — create-scratch/cleanup-scratch wrap the create-group → create-room → archive → move-to-Lobby → delete-group sequence used by both the haiku.garden-j29y load test and the haiku.garden-3fw5 judge-bot spike, so it doesn't get hand-rolled again next time. grant-owner/revoke-owner wrap the operator-role SSH call for other test accounts.

scripts/chatto-testkit.sh create-scratch "my-spike" "throwaway, safe to delete"
scripts/chatto-testkit.sh cleanup-scratch <room_id> <group_id>

Uses demo-client (~/tilde.style/.keys/chatto-demo-bot/credentials), which has been left with a persistent owner role as of 2026-07-09 — testing happens often enough that grant/revoke per session was pure boilerplate. That's a standing privilege escalation on a shared instance, not a throwaway; see haiku.garden-j29y for the decision record.

scripts/chatto-realtime-demo/judge-spike.mjs #

Kept (not a throwaway) — the empirical spike behind haiku.garden-3fw5's resolved trigger model. Subscribes to a room over the realtime WebSocket and replies once every 3 human messages (a placeholder heuristic — real judgment logic is future work), demonstrating what judged/selective participation feels like versus always-on or mention-triggered replies.

CHATTO_DEMO_PASSWORD='...' node scripts/chatto-realtime-demo/judge-spike.mjs <room_id>

Surfaced a real concurrency finding: a plain in-memory counter isn't safe against concurrently-processed events (a reply-in-flight can race a new incoming message) — reinforcing why 3fw5's design calls for a proper room-keyed durable object, not an ad-hoc counter, once this moves past spike stage.

scripts/atlogin-deploy.sh + patches/ (+ ~/src/atlogin) #

Chatto members can log in with their haiku.garden PDS identity via a self-hosted fork of apenwarr/atlogin (MIT) at login.haiku.garden. The fork — two patches on top of pinned upstream — lives at ~/src/atlogin (branch haiku-garden); patches/ holds exported reference copies. See patches/README.md and haiku.garden-j29y for the full story (why atlogin, the login_hint gap, the handle-suffix allowlist, and how that let auto_provision turn on safely for real zero-preprovisioning signup).

scripts/atlogin-deploy.sh build             # local sanity-check build only
scripts/atlogin-deploy.sh deploy            # build + ship + restart; defaults to the haiku.garden VPS
scripts/atlogin-deploy.sh deploy some-host  # or any other host

Dev/build locally, deploy the artifact — the script only builds (cross-compiled GOOS=linux GOARCH=amd64) and ships the binary; it never mutates source on the remote host and never touches the remote state/config.json (secrets, issuer, allowed_handle_suffix), since that's live deployment state, not something to overwrite on every redeploy. Chatto's own [[auth.providers]] client secret (generated via atlogin -new-client chatto) is saved at ~/tilde.style/.keys/chatto-haiku-garden-oidc/credentials.

assets/ #

Wordmark + banner (paper/dusk/moss color variants), grounded in NAVIAR RECORDS' existing visual identity and the project's own "digital gardening" / bento-box framing rather than invented cold.

  • Beans: haiku.garden-a6ay (onboarding script), haiku.garden-dwuy (tranquil-pds deploy) — in this repo's own .beans/, not tracker's
  • Naviar Haiku community PDS, admin: torsten.haiku.garden
  • Project identity: haiku.garden on Bluesky (migrated from haiku-garden.bsky.social 2026-07-03)