f8ab3f965e3390606ce6c363b685fead18bc4b8f
Releases as pull requests, the same in all five app repos (hub, catalog, live, riftbound, karaoke). The Gitea side (PRs only into main, squash merges, required checks, `v*` tags only by release-bot, its `RELEASE_TOKEN` org secret) is dhkatz/infra `modules/app-repo`. - **`.gitea/workflows/pull-request.yml`**: the PR title must be a conventional commit (`feat(api): …`, `fix: …`, `feat!: …`); it becomes the commit on main. `chore(release)` only from release-bot's `release` branch. Required to merge (`Pull request / title`). - **`.gitea/default_merge_message/SQUASH_TEMPLATE.md`**: the squash commit is `<PR title> (#<n>)` plus the description. - **`.gitea/workflows/release.yml`**, on every push to main, as release-bot (`RELEASE_TOKEN`): - if the commit is a merged `chore(release): vX.Y.Z`: tag it vX.Y.Z (lightweight), push the tag (CI builds the image, Komodo deploys prod), publish the release with git-cliff's notes; - otherwise, if anything releasable landed since the last tag: rewrite the `release` branch (CHANGELOG.md) and open or update `chore(release): vX.Y.Z`. - **`cliff.toml`** (git-cliff 2.14.2, pinned by sha512): feat → minor, fix/perf/revert → patch, breaking → minor while 0.x; chore/ci/docs/test/style/build/refactor alone don't release. Only conventional commits count, so the history before this is left out. - **`CHANGELOG.md`**: header only; release PRs fill it. - Agent notes (CLAUDE.md / AGENTS.md): the PR flow and title format; never tag by hand. After merging, `Release` runs on main and should report nothing to release (this PR is `ci:`); the first `feat:`/`fix:` PR merged after it opens the first release PR. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed-on: #1 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.
- 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.
- 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>.