draw things doodl.waow.tech
draw atproto
TypeScript 69%
JavaScript 23%
CSS 7%
HTML 1%

README.md

doodl #

draw something, save it as an image blob on your PDS, share it, and use drawings as the app's own iconography. the drawing app is client-side; Cloudflare Workers handle live rooms, share pages, and optional private-preview synchronization. an explore page reads everyone's drawings straight from their PDSes via your relay + slingshot, so it's a little stale by design.

Lives at https://doodl.waow.tech (static site on your PDS via wisp, custom domain on Cloudflare).

Collections:

  • tech.waow.doodl.drawing — every doodle is an image blob record
  • tech.waow.doodl.iconset — singleton self, mapping UI slots to drawing strongRefs
  • tech.waow.doodl.preferences — singleton self; public defaults (explore, bot feature)
  • tech.waow.doodl.space — a private doodle: a permissioned space you own. add people by handle and draw on it together, live; it saves itself (as ordinary drawing records inside the space) whenever things go quiet. needs a PDS that supports atproto spaces; elsewhere the feature stays dormant.
  • tech.waow.doodl.permissions — the permission set doodl asks for at sign-in (include:), which a spaces PDS expands into the canvas grants

how it works #

  • draw — minimal freehand canvas (pen, color, stroke size, undo, clear), exports PNG. stroke controls live in a bottom sheet so the mobile toolbar stays one row tall.
  • save — com.atproto.repo.uploadBlob → a tech.waow.doodl.drawing record referencing the blob
  • icons — settings writes a tech.waow.doodl.iconset/self record whose assignments point at drawing records; the app renders those slots with the chosen drawings. any slot in the registry can be drawn over, including nav, toolbar, settings, theme, and stroke controls.
  • icon sets — settings can preview someone else's whole iconset across the app. trying one on is local-only until you save it as your own.
  • auth — in-browser AT Protocol OAuth with drawing, icon, preference, live-room and Spaces permissions. OAuth continues to use DPoP; space reads use the current HTTP-signature contract. Private-room credentials renew without interrupting the drawing session.
  • explore — UFOs /records?collection=tech.waow.doodl.drawing (CORS-enabled, firehose-indexed) for the records, then slingshot resolveMiniDoc per repo to build each blob's getBlob URL. (a raw relay's listReposByCollection has no CORS, so it can't be called from the browser.)
  • share — /l/<did>/<rkey> preview pages are served by the Cloudflare Worker in worker/ so crawlers get per-drawing Open Graph image tags
  • bot — optional, opt-in, and deliberately small: bot/ runs the @doodl.waow.tech feature bot for consenting users.

dev #

bun install
bun run dev   # localhost uses the atproto loopback OAuth client
bun run check # typecheck, lint, client and Workers tests
bun run build

deploy #

The Wisp site and custom-domain claim belong to waow.tech, which also publishes the lexicons. The app stays at https://doodl.waow.tech.

one-time: custom domain #

wispctl login waow.tech
wispctl domain claim waow.tech --domain doodl.waow.tech   # prints DNS records
# set those records on the waow.tech Cloudflare zone, then:
wispctl domain add-site waow.tech --domain doodl.waow.tech --site doodl

each release #

bun run deploy:live # live-room Worker
bun run deploy:sync # optional private-preview Worker
bun run deploy      # metadata, build, wispctl deploy --spa
bunx wrangler deploy --config worker/wrangler.jsonc   # share/preview worker
# optional: bunx wrangler deploy --config bot/wrangler.jsonc

See docs for the Spaces access model, integration checks, and cutover procedure.