CI / test (push) Successful in 1m13s
T5 products placed in ordinary IMVU rooms, kept per room instance
(room_decoration, migration 0006). GET /api/v1/decorations/:instance (and
?instances= for up to 20) gives what the viewer may see, with a hash;
PUT is for the room owner's T5DE person only, checks the products exist,
and tells every realtime session with a chat in that room ({"type":
"decor"}). Hosted rooms' IDs are refused.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
438 lines
29 KiB
Markdown
438 lines
29 KiB
Markdown
# T5DE community design
|
|
|
|
Status: draft for review (2026-10-29). Decisions marked **Decided** were agreed in discussion; the rest are
|
|
proposals. Open questions are at the end.
|
|
|
|
## Goals
|
|
|
|
- **Community products**: original work that IMVU's shop doesn't allow, hosted by community-run catalogs, worn and
|
|
placed in T5DE without IMVU ever seeing it.
|
|
- **One account per person** on a central hub, linked to any number of IMVU accounts.
|
|
- **T5DE-hosted rooms**: decorated scenes and their chat, hosted by the hub.
|
|
- **Social extras**: an opt-in T5DE badge, opt-in T5DE presence, creator pages.
|
|
- **Moderation from one place**: the hub's website.
|
|
- **Performance** on every backend.
|
|
- **Free first**, with payments possible later without reworking the data.
|
|
|
|
Non-goals: re-hosting IMVU's products (original work only), changing what IMVU or non-T5DE users see (they see
|
|
IMVU's defaults), and replacing IMVU's servers for anything else (for now).
|
|
|
|
## Overview
|
|
|
|
```
|
|
┌──────────── user's PC ─────────────┐
|
|
│ T5DE client ── patch ── proxy ─────┼──► IMVU (unchanged; never sees community items)
|
|
└────────────────────────────┬───┬───┘
|
|
│ └──► catalogs (many, community-run) ──► S3/R2 storage
|
|
▼
|
|
┌──────────── hub (T5DE-run) ─────────────┐
|
|
│ API · realtime gateway · rooms · jobs │──► IMVU public API (linking, age checks)
|
|
│ website & console · Postgres · NATS · R2│
|
|
└─────────────────────────────────────────┘
|
|
```
|
|
|
|
Repositories (**Decided**):
|
|
|
|
| Repository | What |
|
|
| --- | --- |
|
|
| `t5de` | Client patches; gains removing community items from what IMVU sees, and the new settings. |
|
|
| `t5de-proxy` | The local proxy; gains the product resolver, IMQ router, hub and catalog clients. |
|
|
| `t5de-hub` | The hub: API, realtime gateway, rooms, jobs, website and moderation console (one Go program). |
|
|
| `t5de-catalog` | The reference catalog: API and a small admin page. Anyone can run one. |
|
|
| `t5de-api` | Shared definitions: OpenAPI specs, WebSocket messages, token formats, and Go types generated from them. |
|
|
|
|
## What the client already does (found 2026-10-29)
|
|
|
|
From an experiment with the proxy presenting a made-up product ID as an alias of a real product (t5de-proxy's
|
|
local `spike-alias` branch):
|
|
|
|
- **Products load from whatever the answers say.** Before using a product the client calls `authorizeProducts2`
|
|
(XML-RPC: may this user use it, where are its files, their checksum) and product-info endpoints, then downloads
|
|
the files from the addresses given. When the proxy answered for an ID IMVU doesn't know, the client loaded and
|
|
rendered the product.
|
|
- **IMVU signs requests.** `X-imvu-auth` is md5(user ID + session security key + body), with the query string
|
|
added for some calls. A changed request is rejected ("invalid authorization data"). The proxy sees the key in the
|
|
login response and could re-sign, but the design avoids sending IMVU anything changed on community products'
|
|
behalf: the proxy answers those requests itself, and splits requests that mix both.
|
|
- **Outfit changes are chat commands.** Wearing or removing sends plain-text `*use <ids>` / `*remove <id>`
|
|
messages over IMQ (the home screen is a chat session too), and IMVU relays them to everyone in the session.
|
|
- **IMVU didn't keep an unknown ID** in the account's saved outfit after it was worn.
|
|
- **The proxy holds the IMVU session key.** It never leaves the user's machine: not to the hub, not to catalogs.
|
|
|
|
IMVU's public user API (`api.imvu.com/user/user-<id>`) has `is_ageverified`, `is_ap`, `is_adult` (self-reported
|
|
age, not verified) and `tagline`.
|
|
|
|
## Client side
|
|
|
|
### Patch (`t5de`)
|
|
|
|
- Removes community product IDs from outgoing `*use` / `*remove` and outfit saves, so IMVU and non-T5DE users see
|
|
defaults. Tells the proxy the full outfit through a local endpoint only the proxy answers.
|
|
- Settings: T5DE badge, T5DE presence, adult content (each opt-in), hub sign-in.
|
|
|
|
### Proxy (`t5de-proxy`)
|
|
|
|
- **IMVU passthrough**: as today.
|
|
- **Product resolver**: community products are named (global product ID, revision) everywhere outside the machine.
|
|
The client only understands 32-bit integers (XML-RPC), so the proxy maps each to a local ID in a reserved range
|
|
near the top (about 1.9-2.1 billion), stored per install. It answers the client's permission, product-info and
|
|
file requests for them, and splits mixed requests (the IMVU part forwarded unchanged).
|
|
- **File cache**: files named by sha256, kept on disk; worn items keep working when a catalog is down.
|
|
- **IMQ router**: chat for T5DE-hosted rooms goes to the hub, the rest to IMVU. Adds other T5DE users' community
|
|
items to incoming `*use` messages, so the client's own code loads them.
|
|
- **Hub client**: one WebSocket for presence, room channels and hosted rooms; device-code sign-in, refresh token
|
|
encrypted for the Windows user (DPAPI).
|
|
- **Local pages**: browsing and account pages shown in the client's built-in browser.
|
|
- **Creator live preview**: loads a product from a local folder and reloads it as files change.
|
|
|
|
## Identity (**Decided**)
|
|
|
|
- A hub **account is a person**. It links to any number of **IMVU accounts**, up to a cap stored per account
|
|
(default 10), which moderators can raise on request.
|
|
- **Linking**: the hub gives a one-time code, the user puts it in their IMVU tagline, the hub reads it from IMVU's
|
|
public API. The user can remove it afterwards. **The most recent proof wins**: linking an account already linked
|
|
elsewhere moves it (accounts get sold or given away), and the old link ends.
|
|
- **Sign-in**: device-code flow (approve in a browser). Tokens are short-lived Ed25519 JWTs with key IDs; keys
|
|
rotate and are published as a JWKS.
|
|
- **Alts stay private**: tokens for catalogs and rooms carry an ID per linked IMVU account (pairwise), never the
|
|
person's ID, so other people and catalogs can't connect a person's accounts. Only the person and moderators can.
|
|
- **Creators** have a public creator profile, separate from (and not revealing) their IMVU accounts.
|
|
- **Switching accounts in the client**: the proxy sees which IMVU account logs in and tells the hub. Not linked:
|
|
offer to link. Linked to another hub account: no community features that session.
|
|
- **Bans** apply to the person, so all linked accounts.
|
|
|
|
## Age (**Decided**: free, several providers, vendor-neutral)
|
|
|
|
No payment for verification (adult content rules out mainstream merchants of record; see the discussion of
|
|
2026-10-29). Several ways to prove age, tried in order of privacy, behind one interface so any can be swapped:
|
|
|
|
1. **IMVU** (live): the IMVU account is age verified (`is_ageverified`) or has Access Pass (`is_ap`), read by the hub
|
|
from IMVU's public API. Scope: that IMVU account. Re-checked about daily; a revocation shows at the next check.
|
|
Self-reported `is_adult` doesn't count.
|
|
2. **Phone wallets** (planned): a government ID in Google Wallet, Apple Wallet or an EU age-verification wallet,
|
|
asked for "age over 18" only, through the browser's Digital Credentials API. Free for the hub; no documents or
|
|
birth date reach it. Scope: the person.
|
|
3. **ID document and selfie** (planned): Didit's hosted check, within its free monthly allowance. Scope: the person
|
|
(the liveness step stops someone using another person's ID).
|
|
|
|
### How it's built
|
|
|
|
- **One table of results**, `age_check`: subject (a person, or one of their linked IMVU accounts), provider,
|
|
method (e.g. `imvu_age_verified`, `imvu_access_pass`, `wallet_mdoc`, `id_document`), status (`pending`,
|
|
`complete`, `failed`, `revoked`), over 18 yes/no, the provider's reference, when it was checked and when it
|
|
expires. Never a birth date, document, photo or name: providers that see those keep them.
|
|
- **One rule**: an IMVU account counts as adult if its person has a current complete check that says over 18, or
|
|
the account itself has one. Everything (website, tokens for catalogs, room channels) asks this one function.
|
|
- **Providers** implement a small interface: `start` (begin a check: done at once, a page to send the person to,
|
|
or a request for the browser to hand to a wallet), `complete` (finish from the provider's callback or signed
|
|
webhook), and optionally `recheck` (for providers whose answer can change, like IMVU). Which ones run is
|
|
configuration (`AGE_PROVIDERS`), with each provider's keys in its own settings. Adding or replacing a vendor is
|
|
one new adapter; nothing else changes.
|
|
- Webhooks arrive at `/api/v1/age/webhook/<provider>` and each provider checks its own signature.
|
|
- Enforced at three points: catalogs (adult files need an adult token), the hub (adult items left out of room
|
|
channels for viewers who aren't adult and opted in), and the viewer's proxy.
|
|
|
|
### To add the planned providers
|
|
|
|
- Didit: an account, an API key and webhook secret, and a workflow set to ID document plus liveness, returning
|
|
only "over 18". Confirm its free monthly allowance covers age checks.
|
|
- Wallets: Google's Digital Credentials verifier registration (free), and an `age_over_18` request (OpenID4VP,
|
|
mdoc); Apple and the EU wallet through the same browser API where supported.
|
|
|
|
## Products and catalogs
|
|
|
|
- **The hub is the registry.** Products have **global IDs** owned by their creator. A **revision** is a file list
|
|
(path, sha256, size), signed by the hub on the creator's behalf (creator-held keys later). Revisions never change.
|
|
- **Catalogs host and curate**: their own rules, ratings ceiling and community. A catalog stores files and tells the
|
|
hub which revisions it hosts. The same revision on several catalogs is mirrored, not duplicated: uploading a file
|
|
list identical to an existing revision links to it. Variants link with **derived from** (credit, and the original
|
|
creator's derive permission).
|
|
- **Outfits and rooms refer to products and revisions** (see Revisions), so any catalog hosting a revision can serve
|
|
it.
|
|
- **Files** are named by hash, immutable, served from object storage or a CDN; never through Go.
|
|
- **Ratings**: general and adult, as IMVU. Catalogs declare the highest rating they host.
|
|
- **Uploads** go from the creator's browser (or the proxy) straight to the catalog's API with a hub token, so large
|
|
files never pass through the hub. The catalog validates them: formats parsed, size limits, nothing executable,
|
|
thumbnails generated, extra texture maps accepted (see Better looks).
|
|
- **Format**: IMVU's own product files, as the client loads them, so creators keep using IMVU's tools.
|
|
|
|
### Revisions (**Decided**: public history)
|
|
|
|
A **revision** is the product itself: its files. IMVU has revisions too (its file addresses carry a revision
|
|
number, `productdata/<id>/<revision>/`) but hardly shows them; T5DE shows them openly.
|
|
|
|
- **Numbered** 1, 2, 3... per product. Uploading changed files makes a new revision; editing info doesn't.
|
|
- **History on every product's page**, for everyone: each revision's number and date, the creator's notes (what
|
|
changed, required when publishing), a preview image, the files added, removed or changed (with sizes), its
|
|
rating, and which catalogs host it. Any two revisions can be compared.
|
|
- **Which revision people get** (**Decided**): always the **latest**, everywhere, by default. A person can opt in,
|
|
per product, to **stay on** a revision they prefer: a setting on the product's page, kept per person (all their
|
|
IMVU accounts), and only for what they wear. Rooms always use the latest revision of their furniture. Outfits and
|
|
scenes store just the product; the proxy picks the revision when loading (the pin if there is one, else the
|
|
latest), and what a wearer broadcasts in a room is always a concrete revision, so everyone sees the same one.
|
|
- **Old revisions stay available**, so outfits and rooms keep working. A creator can **retract** a broken
|
|
revision: it's marked retracted in the history, and anyone following or staying on it moves to the nearest
|
|
revision that isn't. Only moderation removes a revision outright (shown as "removed by moderation", with the
|
|
files gone).
|
|
- **Derived from** points to a specific revision of the original.
|
|
- Moderators see the same history, plus who uploaded each revision and from where.
|
|
|
|
### Product info (**Decided**: editable)
|
|
|
|
- A product's **files** come in revisions that never change; its **info** belongs to the product and its creator
|
|
can edit it at any time on the hub's website: name, description, icon, tags, category. Stored at the hub, so an
|
|
edit shows everywhere (every catalog, every client) without re-uploading anything.
|
|
- **Icon**: an image uploaded to the hub, re-encoded there (decoded and saved again as PNG or WebP at fixed sizes,
|
|
which drops anything hidden in the file) and served from R2.
|
|
- **Description**: HTML, as on IMVU, made safe by an **allowlist** (in Go, bluemonday or equivalent), never a
|
|
blocklist:
|
|
- Tags: text formatting (`b`, `i`, `u`, `s`, `em`, `strong`, `small`, `big`, `sub`, `sup`, `font` with `color`
|
|
and `size`, `span`, `div`, `p`, `br`, `hr`, `center`), lists, simple tables, headings, `a`, `img`.
|
|
- Styles: only a short list of properties (color, background color, font size, weight and style, text
|
|
alignment and decoration) with checked values; no `url()`, no positioning, nothing that can cover the page.
|
|
- Links: `http`/`https` only, with `rel="nofollow ugc noopener noreferrer"`, opening outside the page.
|
|
- Images: `https` only, fetched through the hub's image proxy (re-encoded, size-limited), so viewers' addresses
|
|
don't reach outside servers and pages don't mix in insecure content.
|
|
- Everything else is removed: scripts, event attributes (`onclick`, ...), `iframe`, `object`, `embed`, `form`,
|
|
`style` and `link` elements, `javascript:` and `data:` addresses, comments.
|
|
- Size limits on the description.
|
|
- **Cleaned twice**: when saved (the stored HTML is already safe) and again when shown, by whatever shows it: the
|
|
hub's website (with a strict Content-Security-Policy that allows no inline scripts) and the proxy.
|
|
- **In the client, the proxy is the last line**: IMVU's pages run in Gecko 1.9.2 with chrome privileges (they can
|
|
call into the client's Python), so a script there could do far more than on a website. The proxy cleans
|
|
descriptions again with the same allowlist before handing them to the client, and gives the client text-only
|
|
fields (name, tags) as escaped text.
|
|
- **History and moderation**: every edit is kept as a revision; moderators see the history and can revert or lock
|
|
a product's info. Changing the rating to *adult* is immediate; changing it to *general* is reviewed first.
|
|
|
|
### Catalogs (`t5de-catalog`)
|
|
|
|
- Go program, SQLite or Postgres, S3-compatible storage, docker-compose for operators.
|
|
- **API**: catalog and search, revisions, files by hash, uploads, reports, takedowns.
|
|
- **No user-facing site** (**Decided**): users, creators and operators use the hub's website. Operators' rights
|
|
come from hub tokens ("operator of catalog X"); the catalog enforces them itself, so operators stay in charge of
|
|
their server.
|
|
- **Small admin page** for what must work without the hub: first setup, storage, keys, registering with the hub,
|
|
health, emergency takedown.
|
|
- General-audience files are public (cacheable, and catalogs learn less about who wears what); adult files need a
|
|
token.
|
|
- Operators can build their own branded site on the public API.
|
|
|
|
### Joining the hub and access (**Decided**)
|
|
|
|
- **Any catalog can ask to join** the hub. It's hidden until a hub moderator approves it.
|
|
- To join, a catalog must give **its own takedown and legal contact**. It stores the files, so the hub forwards
|
|
takedown notices and reports about its products to that contact (and can still block a product network-wide).
|
|
- Two settings per catalog:
|
|
- **Visibility**: *public* (listed in the hub's directory and search) or *private* (not listed; found through an
|
|
invitation or link).
|
|
- **Membership**: *open* (any hub user can use it), *approval* (users ask, the catalog's operators approve) or
|
|
*invite only*.
|
|
|
|
Any combination works: a public catalog can still require approval; a private one can be open to anyone who has
|
|
the link.
|
|
- **Seeing versus wearing** (**Decided**): anyone can *see* any catalog's products on someone else (subject to their
|
|
own age settings); getting and wearing them needs membership where the catalog requires it.
|
|
|
|
## In the client's shop and inventory (planned, the user's goal)
|
|
|
|
- **"Buying" is saving**: getting a T5 product (free) adds it to the person's library on the hub. The proxy shows
|
|
library products in the client's own inventory, next to IMVU's, so they're worn like any other.
|
|
- **Browsing in the shop**: T5 products appear in the client's shop, with a T5 filter beside IMVU's rating filter
|
|
and a T5 badge on T5 products (inventory, shop, product cards). Images are drawn and kept for this (the user's
|
|
Pictures\T5DE badges, made by its `draw.py`): badges 18x14, 25x21 and 23x19; filter strips for IMVU's two
|
|
layouts with T5 added last (`GA|all|AP|T5`, `GA|AP|AP+|all|T5`) and `all|T5` for people without Access Pass,
|
|
who get no rating filter today (T5 products aren't only adult ones).
|
|
- **Where the T5 badge goes** (every place the client shows AP/AP+/VIP badges, checked in the client's UI files):
|
|
- *Products and outfits* (always, a property of the product): shop results and product pages, inventory,
|
|
the outfits viewer and outfit cards (an outfit with any T5 product gets it, as AP outfits do), product
|
|
recommendation widgets, checkout ("getting" a T5 product).
|
|
- *People* (only if they turned on "Show T5DE badge" for that IMVU account, the same choice everywhere): avatar
|
|
cards, the friends list (page and sidebar), the right-click menu on an avatar, chat names, the inbox, the
|
|
"invited to chat" and Shop Together invite dialogs, the gifting friend picker, home, and Find People and Get
|
|
Matched (with a "T5DE users" filter there).
|
|
- *Rooms* (always, not optional: visitors need to know a room needs T5DE to be seen as meant; hosted rooms, and
|
|
IMVU rooms decorated with T5 products): the chat rooms browser, room cards, the owner's room manager, and the
|
|
room's tab.
|
|
The client learns who and what is T5 from the proxy, which asks the hub (badge choices, products, rooms).
|
|
- Needs: the hub's product registry and catalogs serving files (so products don't have to be local packs), the
|
|
library on the hub, the proxy answering the inventory's and shop's calls for T5 products, and a patch to the
|
|
shop page for the filter and badges.
|
|
|
|
## Outfits
|
|
|
|
1. The user puts on a community item. The client shows it (proxy answers for it); the patch sends IMVU the outfit
|
|
without it.
|
|
2. The proxy sends the full outfit to the hub, which keeps the current outfit per linked IMVU account and relays
|
|
it on the room channel.
|
|
3. Other T5DE users' proxies add the items to that user's incoming `*use`, filtered by their own age settings.
|
|
Non-T5DE users see the default.
|
|
|
|
How it's built (2026-10-29):
|
|
- The proxy sees the client's IMQ subscriptions to chat queues (`/chat/<id>`) and tells the hub which chats it's
|
|
in; for a room's chat, also the room (from `chat.getOrMakeChat`, `activity: publicroom-<owner>-<n>`).
|
|
- The hub tells each proxy in a chat about the other T5DE users there and what they wear (adult products only to
|
|
clients whose account counts as adult), on joining and on every change.
|
|
- The proxy delivers `*use` / `*remove` lines from those users to the client (IMQ messages of its own, without a
|
|
sequence number, which the client doesn't track), and adds their community products to the whole outfits they
|
|
send (`*use` with the body, re-sent on joins and every 30 s or so), which would otherwise put IMVU's back.
|
|
- Products the viewer has no pack for are skipped (logged), until catalogs serve them.
|
|
- The client also posts its outfit to IMVU's chat participant endpoint (`legacy_outfit_message`); the proxy takes
|
|
community products out of it and re-signs it.
|
|
- Anyone signed in can say they're in a chat; what they learn is what T5DE users in it wear, which anyone in the
|
|
chat sees anyway.
|
|
|
|
Saved outfits are per IMVU account (avatars differ). The library (products added, favorites, followed creators)
|
|
is per person, shared across their accounts.
|
|
|
|
## Rooms (**Decided**: both)
|
|
|
|
- **Decorations over IMVU rooms**: community furniture placed in an IMVU room, seen only by T5DE users. The hub keeps
|
|
them per room; proxies strip them from what IMVU gets and add them back on load (products.md, "Decorations").
|
|
- **T5DE-hosted rooms**: the room lives on the hub: scene, furniture placement, owner, moderators, permissions,
|
|
bans, chat, seats, presence. The proxy routes that room's IMQ traffic to the hub, which speaks the subset of
|
|
IMQ the client needs (subscribe, state changes, mounts, messages) and the room-joining XML-RPC calls, so the
|
|
client treats it as an ordinary room. Needs our own definitions of those messages, written from observation.
|
|
- **Chat retention** (**Decided**): not stored by default. When someone reports a conversation, the recent messages
|
|
around the report are kept as evidence for 30 days, then deleted.
|
|
- Room shells and furniture are ordinary products.
|
|
- **Rooms list** (2026-10-29, the user's request): the hub's Rooms page lists IMVU rooms of T5DE users that are
|
|
public, listed and not expired on IMVU (`privacy: public`, `type: listed`), and whose owner turned on "List my
|
|
public rooms" for that IMVU account (off by default, like the badge). IMVU's public API can't list a user's rooms,
|
|
so the hub learns of a room when a T5DE user enters it, asks IMVU about it, and asks again hourly while it's
|
|
used; rooms nobody entered for 30 days drop off. AP rooms are shown only to people with an adult account. Each
|
|
shows the T5DE users in it now who show they're online.
|
|
|
|
## Presence and badge (**Decided**: both opt-in, per IMVU account)
|
|
|
|
- **Badge**: other T5DE users see that this account uses T5DE. The proxy asks the hub which of the accounts it's
|
|
showing have it (`POST /api/v1/badges`, docs/api.md).
|
|
- **Presence**: friends see "online in T5DE". The proxy asks the hub about the user's IMVU friends; the hub doesn't
|
|
store friend lists, but it sees them when asked (stated in the privacy text).
|
|
|
|
## Better looks
|
|
|
|
Revisions can carry extra texture maps (normal, roughness, ...) beside the standard ones. The proxy hands them to
|
|
T5DE's renderer, which uses them when drawing the item; IMVU products and other clients get the standard textures.
|
|
Hub and catalogs only store and label the files; the work is in t5de-renderer.
|
|
|
|
## Creator pages
|
|
|
|
Profile, products across all catalogs, follows, and release notifications (over the realtime gateway).
|
|
|
|
## Moderation and policy
|
|
|
|
- **One policy at the hub**, binding every catalog to stay listed: original work only; no logos, characters or
|
|
real people's likenesses; zero tolerance for sexualized depictions of minors, with the legal reporting duties
|
|
that come with it; a takedown process and contact.
|
|
- **Levers**: delist a catalog; block a product (global ID, and its file hashes to catch re-uploads) network-wide,
|
|
pushed to every proxy; ban a person; close a hosted room; mute or ban in rooms.
|
|
- **Console** on the hub's website: report queue, live views (rooms, reports) over server-sent events, actions,
|
|
audit log. Catalog operators get their catalog's queue there too.
|
|
|
|
## Payments later (**Decided**: free first)
|
|
|
|
Built in from the start so payments can be added without reworking data: getting a product creates an
|
|
**ownership record** (source "free", price 0); products have a price (always 0); creators have an empty payout
|
|
field. A payment service plugs in later.
|
|
|
|
## Hub (`t5de-hub`)
|
|
|
|
One Go program in modules (split into services only if load requires it):
|
|
|
|
- **Identity**: accounts, linked IMVU accounts, link caps, device-code sign-in, tokens, JWKS.
|
|
- **IMVU checks**: background jobs reading IMVU's public API (tagline when linking; `is_ageverified` when linking
|
|
and at most daily). Called rarely and cached: IMVU could block the hub's address, and nothing but linking may
|
|
depend on IMVU being up.
|
|
- **Registry**: products, revisions, ratings, derivations, catalogs and what they host, search (Postgres full-text
|
|
first; Meilisearch when needed).
|
|
- **Realtime gateway** (WebSocket): presence, badge, room channels, hosted rooms. Live room state in memory on the
|
|
node owning the room; NATS passes it between nodes once there's more than one.
|
|
- **Rooms**: hosted rooms' stored state.
|
|
- **Moderation**: reports, actions, blocklist, audit log.
|
|
- **Website and console** (**Decided**: in this repo): browse catalogs, products, creators, rooms, news; manage
|
|
accounts (links, privacy, data export and deletion); catalog operators' screens; moderation console.
|
|
**Decided**: server-rendered. Built with Go's html/template (no code generation step) and htmx, embedded in the
|
|
program: fast, cacheable at Cloudflare, indexable, no JS build. How it looks comes from its CSS (a small design system of our own), not from
|
|
rendering on the server; small scripts where a page needs richer interaction (image galleries, drag and drop).
|
|
|
|
### Data (sketch)
|
|
|
|
```
|
|
person(id, link_cap, settings, banned_at)
|
|
imvu_link(person, imvu_id, pairwise_id, linked_at, age_verified, age_checked_at, badge, presence)
|
|
creator(id, person, name, page)
|
|
catalog(id, url, operators, max_rating, status, signing_key)
|
|
product(id, creator, rating, slot, derived_from, price)
|
|
revision(product, number, manifest_sha256, signature, notes, created_at, status, extra_maps)
|
|
hosting(catalog, revision)
|
|
ownership(person, product, source, price)
|
|
pin(person, product, revision)
|
|
outfit(imvu_link, items[product])
|
|
room(id, owner, scene, furniture[], roles, bans)
|
|
report(...), moderation_action(...), audit(...)
|
|
```
|
|
|
|
## Infrastructure
|
|
|
|
- **Hub**: two small VMs or containers behind Cloudflare (already in front of Gitea; supports WebSockets),
|
|
managed Postgres with point-in-time backups, R2 for the hub's own assets and T5DE's example catalog (no egress
|
|
fees). Jobs in Postgres (River). NATS when the gateway has more than one node.
|
|
- **Deploys**: Gitea CI builds container images and runs migrations; OpenTofu once there are more than a couple
|
|
of machines.
|
|
- **Monitoring**: Bugsink (already used for crash reports) for server errors; Prometheus and Grafana (or Grafana
|
|
Cloud) for metrics; a status page.
|
|
- **Limits**: rate limits at Cloudflare and per account; upload limits at catalogs.
|
|
|
|
## Performance
|
|
|
|
Go throughout. Product files never pass through Go (hash-named, from a CDN or object storage). pgx with sqlc (plain
|
|
SQL, no ORM) and indexes designed up front. Public pages cached at Cloudflare. Live rooms in memory on their node.
|
|
The proxy streams and adds no waiting.
|
|
|
|
## Failure
|
|
|
|
- **Hub down**: community items fall back to IMVU's defaults; everything IMVU keeps working; hosted rooms are
|
|
unavailable.
|
|
- **Catalog down**: its items come from the proxy's cache or another catalog hosting the same revision, else
|
|
defaults.
|
|
- **IMVU's API down**: linking and age checks wait; existing links and cached checks keep working.
|
|
|
|
## Privacy
|
|
|
|
- IMVU session keys never leave the user's machine.
|
|
- Linked accounts are private (pairwise IDs).
|
|
- The hub sees room presence and, for presence, the friends asked about; it stores neither beyond what's needed.
|
|
- Catalogs see addresses of those fetching adult files (with a token) and of uploaders.
|
|
- All stated in the setup and on the website.
|
|
|
|
## Status
|
|
|
|
- Phase 1 (local packs) done: t5de-api, t5de-proxy `internal/products`.
|
|
- Phase 2 (hub identity) under way: accounts, tagline linking, age (vendor-neutral, IMVU's re-checked daily), link
|
|
caps, device sign-in, presence and badge, console, the room channel and the rooms list. Not yet: a sign-in button
|
|
in the client (the proxy's `signin` command for now), deployment.
|
|
- Hosted rooms (phase 5, docs/hosted-rooms.md): the hub's side is done (rooms made on the website, REST, the
|
|
realtime `room.*` protocol, tested with fake proxies); the proxy's side is under way. The room channel is tested with
|
|
fake proxies and one live client; two live clients need a second throwaway account.
|
|
- **Stack changed** (2026-10-29, the user's choice): the hub is a Bun monorepo: Elysia API, Better Auth, Drizzle on
|
|
Postgres, Next.js website with Tailwind and shadcn/ui (README). It replaces the first version, in Go. Where this
|
|
document says Go or templ for the hub, read that; catalogs are still to be decided (a Bun program compiled to one
|
|
file would keep them easy to run).
|
|
|
|
## Phases
|
|
|
|
1. Spec (`t5de-api`); local product packs, removing community items from what IMVU sees, creator live preview. No
|
|
servers.
|
|
2. Hub identity: linking, age checks, link caps, badge, presence; website basics.
|
|
3. Registry and catalogs: creator pages, community outfits visible to T5DE users, moderation console.
|
|
4. Decorations over IMVU rooms.
|
|
5. T5DE-hosted rooms.
|
|
6. Better looks (renderer work; can run alongside 3 or later).
|
|
7. Later: payments.
|
|
|
|
## Open questions
|
|
|
|
- The takedown contact and legal entity for the hub itself.
|
|
- Hosted rooms: how much of IMQ and the room-joining calls the client needs (see docs/imq-rooms.md).
|