2cbedd26fcdc3edcf18df498088f041477028638
Phase 1 of `docs/console.md` (builds on #6, whose docs commit it includes; merge #6 first). **API** - `permissions.ts`: `resource:action` permissions, built-in roles as bundles at a scope (hub, catalog, product), `can`/`require`, "may give" checks. A missing permission answers 403 with `missing: {permission, scope}`. - Catalog management: settings (incl. rating ceiling, upload policy, mirroring), key rotation, operators, membership (open / approval / invite links, bans), delisting, suspension by moderators, and `GET /api/v1/catalog/changes` for catalogs. - Membership is enforced: getting a product (403 `members_only` with the catalogs to join), the library list, and outfits relayed to others. Seeing products still needs no membership. - Console for everyone who controls a catalog, product or hosted room; `/console/audit` (tagged entries, scoped by permission, with filters), `/console/permissions`; `/me` says whether the console is for them. - Migration 0010: `role_assignment`, `catalog_member`, `catalog_invite`, `catalog_change`, catalog settings, audit `tags` (backfilled from old entries). **Website** - Console overview by what you manage; audit log page with filters; catalog list scoped to your teams; a catalog management page (settings, key, team, members, invitations, products, moderation, recent changes). - Public catalog pages with join / ask / accept invitation / leave; product pages say "members only" and link the catalogs. **Checked**: 43 API tests pass (8 new in `catalogs.test.ts`); API, web and db type checks; every new page rendered against a local hub for owner, operator, member, outsider and admin; the Eden DELETE-with-query calls tested against the API. Server-action forms weren't clicked through in a browser. Not in this PR (later phases): catalog deletion requests (with cases), drafts and moderator product edits, notifications, API tokens, two-factor, imports, charts. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Phase 1 of `docs/console.md` (builds on #6, whose docs commit it includes; merge #6 first). **API** - `permissions.ts`: `resource:action` permissions, built-in roles as bundles at a scope (hub, catalog, product), `can`/`require`, "may give" checks. A missing permission answers 403 with `missing: {permission, scope}`. - Catalog management: settings (incl. rating ceiling, upload policy, mirroring), key rotation, operators, membership (open / approval / invite links, bans), delisting, suspension by moderators, and `GET /api/v1/catalog/changes` for catalogs. - Membership is enforced: getting a product (403 `members_only` with the catalogs to join), the library list, and outfits relayed to others. Seeing products still needs no membership. - Console for everyone who controls a catalog, product or hosted room; `/console/audit` (tagged entries, scoped by permission, with filters), `/console/permissions`; `/me` says whether the console is for them. - Migration 0010: `role_assignment`, `catalog_member`, `catalog_invite`, `catalog_change`, catalog settings, audit `tags` (backfilled from old entries). **Website** - Console overview by what you manage; audit log page with filters; catalog list scoped to your teams; a catalog management page (settings, key, team, members, invitations, products, moderation, recent changes). - Public catalog pages with join / ask / accept invitation / leave; product pages say "members only" and link the catalogs. **Checked**: 43 API tests pass (8 new in `catalogs.test.ts`); API, web and db type checks; every new page rendered against a local hub for owner, operator, member, outsider and admin; the Eden DELETE-with-query calls tested against the API. Server-action forms weren't clicked through in a browser. Not in this PR (later phases): catalog deletion requests (with cases), drafts and moderator product edits, notifications, API tokens, two-factor, imports, charts. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: dhkatz <[email protected]>
t5de-hub
T5DE's hub: accounts linked to IMVU accounts, presence, and (to come) the registry of community products and catalogs, T5DE-hosted rooms, and the moderation console. Part of T5DE (git.t5de.dev/t5de). Design: docs/design.md; the API: docs/api.md; what a room needs from a server: docs/imq-rooms.md.
What works
- Accounts (Better Auth): sign-up with email, username and password; sign-in by username. The first account is
an admin; roles user, moderator, admin. Registration open or closed (new accounts wait for a moderator's
approval): an admin's setting in the console, defaulting to the API's
REGISTRATION. - Linking IMVU accounts: the person puts a code in the account's tagline; the hub reads it from IMVU's public API. The most recent proof wins (an account linked elsewhere moves, with a new pairwise ID). Link caps per person (default 10).
- Age: an IMVU account counts as adult if IMVU says it's age verified or has Access Pass, checked by the hub, or
if its person verified their age with an ID document and a selfie (Didit, when configured:
AGE_PROVIDERS, seeapps/api/src/index.tsand docs/design.md, "Age"). - Proxy sign-in: OAuth 2.0 device authorization (
t5de-proxy signinshows a code to enter at/device); the proxy then uses its session as a bearer token. JWTs with published keys (/.well-known/jwks.json) for catalogs. - Presence: signed-in proxies keep a WebSocket (
/api/v1/realtime) telling the hub which IMVU account is in use and the community products it wears./onlineshows those who opted in, live, with the opt-in T5DE badge. - Products (website): browsing and search, product pages, uploads straight from the browser to a catalog,
editing, revisions (retract, stay on one), the library, and registering catalogs (
docs/products.md). Pages send a strict Content Security Policy (apps/web/proxy.ts). - Console (moderators): counts, who's online (live), people, link caps, bans, roles, the audit log.
Layout
A Bun monorepo (workspaces, Turborepo):
apps/api |
The API: Elysia on Bun, Better Auth, the realtime WebSocket. Port 4101. |
apps/web |
The website: Next.js 16 (React server components), Tailwind CSS 4, shadcn/ui. Port 4100. Calls the API through its own /api (rewrites), typed end to end with Eden. |
packages/db |
Drizzle schema and migrations; Postgres in production, PGlite (in-process Postgres) for development and tests. |
Development
Needs Bun (1.4) and Node (for Next.js).
bun install
bun run dev # the API on :4101 (database in apps/api/data) and the website on :4100
bun run test tests the API against PGlite; bun run check-types, bun run build.
Running it
- Self-hosted:
AUTH_SECRET=$(openssl rand -base64 32) docker compose up -druns Postgres, the API and the website (seedocker-compose.ymlfor the public addresses). - Elsewhere: the website deploys as is to Vercel and similar (
apps/web, envAPI_URL); the API to any host that runs Bun or containers (Railway, Render, Fly.io, Heroku), withDATABASE_URL,AUTH_SECRET,API_URL(its public address) andWEB_URL(the website's).
A staging site shows a slim "Staging" bar: when it's served from a staging. address (hub.staging.t5de.dev),
or with the website's HUB_ENVIRONMENT=staging (production hides it).
T5DE's proxies reach the API's address directly (sign-in and the WebSocket): t5de-proxy signin -hub <API address>.