☆ twinkl.social ☆#
the chaos of geocities · the drama of myspace · the heart of neopets
Twinkl is a social platform built on ATProto that fuses the chaotic creativity of the early web — GeoCities home pages, MySpace customization, Neopets-style pets, LiveJournal blogging, and Y2K chatrooms — into one nostalgic, deliberately cringe, beautiful amalgamation.
Features#
- Feeds — ATProto microblogging timeline + composer.
- My Top Friends — pick your top friends; they get a dedicated feed and highlighted posts.
- Home page — a fully customizable bento grid of Web Tiles, plus optional custom CSS.
- Guestbook — visitors leave comments that show up on your page.
- Songs — attach songs that play when someone visits.
- Chat — direct messages.
- Blogging — long-form posts surfaced from standard.site.
- Themes — built-in
y2kringe(dark neon) andgyaru(light), your own custom CSS, and a shareable theme gallery. - Lists, notifications, saved posts, moderation, account settings — full Bluesky-parity surfaces alongside the above.
Develop#
pnpm install
pnpm dev # http://127.0.0.1:5179
Before committing: pnpm check (0 errors / 0 warnings), pnpm format, pnpm lint, pnpm build,
and pnpm test. See AGENTS.md § Per-phase code-review gate for the full gate.
Architecture#
Twinkl is a browser-OAuth SPA: the browser holds the OAuth session (tokens + DPoP keys in IndexedDB) and talks to atproto directly — there is no SQLite, no server-held session, and no server-side data cache. The SvelteKit server is a stateless shell: static assets, a meta-only SSR shim for a handful of public routes (crawler-safe OG cards + first paint), the OAuth client-metadata route, and a tiny confidential-client assertion signer. See AGENTS.md for the full feature-sliced architecture, slice boundaries, lexicons, and (critically) § 9's security model.
Deploy#
docker compose up -d --build # app (stateless) + the DASL tiles-loader sidecar
Point a host-installed reverse proxy at the published 127.0.0.1 ports — see
deploy/host-nginx.conf.example and nginx.conf's comments
for why the tile loader should live on a separate registrable domain from the app. Configure
via environment variables (see .env.example):
TWINKL_PUBLIC_URL— the app's public https origin (OAuth client_id/redirect origin).TWINKL_OAUTH_PRIVATE_KEY— optional; set to run a confidential OAuth client (the assertion signer signs with it). Unset falls back to a public OAuth client.PUBLIC_TILE_LOAD_DOMAIN/TILE_BASE_HOST— optional; enable loaded/external (DASL) Web Tiles. Unset disables loaded-mode tiles (inline tiles are unaffected).
docker compose --profile demo up -d --build (or ./start.sh) additionally starts a
containerized nginx + Cloudflare quick tunnel for a zero-DNS local demo.
License#
TBD.