CI / test (push) Successful in 6m38s
The realtime connection now carries the IMVU chats a client is in: the hub tells each proxy in a chat which other T5DE users are there and what community products they wear (adult products only to adult accounts), on joining and on every change. Rooms T5DE users enter are looked up on IMVU; those that are public, listed and whose owner turned on "List my public rooms" appear on the new Rooms page, with the T5DE users in them who show they're online. AP rooms are shown to adults only. Co-Authored-By: Claude Opus 5.5 <[email protected]>
410 lines
26 KiB
Markdown
410 lines
26 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.
|
|
|
|
## 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. Placements
|
|
travel on the room channel like outfits.
|
|
- **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.
|
|
- **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. 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).
|