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 embedshaiku-poet.json— Bluesky presence + a featured haikugeneral.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.
Related #
- 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.gardenon Bluesky (migrated fromhaiku-garden.bsky.social2026-07-03)