The hub registers catalogs (hidden until a moderator approves them), hands out upload tokens, and records the revisions catalogs register after validating uploads: numbered, never changed, signed by the hub (its own Ed25519 key, published at /.well-known/t5de-keys.json). Creators edit product info (descriptions cleaned by an allowlist) and can retract revisions. People get products into their library, optionally staying on a revision; the proxy asks which revision to load and where. Contract in docs/products.md. Co-Authored-By: Claude Opus 5.5 <[email protected]>
6.3 KiB
Products, catalogs and the library: the contract
How the hub (registry), catalogs (t5de-catalog, file hosting) and T5DE's proxy work together. The design and the
reasons are in design.md ("Products and catalogs", "Revisions", "In the client's shop and inventory"); this is
what each side sends.
Words
- Product: a T5 product, owned by its creator (a hub person). ID: 10 characters of
a-z2-7(random), e.g.k3mq7xa2pd. Valid as a pack ID, so the proxy can store a downloaded product as a pack. - Revision: a product's files, numbered 1, 2, 3... Never changes. Its file list is
[{name, sha256, size}], sorted by name;sha256is lowercase hex. - Catalog: a server storing files by sha256 and serving them. Registered on the hub; hidden until a hub moderator approves it.
- Library: the products a person "got" (free), per person (all their IMVU accounts), optionally staying on a revision.
Tokens
The hub signs tokens with its own Ed25519 key (EdDSA JWTs, compact form), published as a JWKS at
GET /.well-known/t5de-keys.json (kid in each token's header). Catalogs fetch it (and again when they see an
unknown kid). Claims always include iss (the hub's API URL), typ (below) and iat.
typ |
Other claims | Lifetime | Use |
|---|---|---|---|
t5de-upload |
sub (person ID), catalog (catalog ID), product (optional: a new revision of this product) |
30 min | the creator's browser uploads to the catalog |
t5de-access |
sub (person ID), adult (bool) |
1 h | the proxy fetches adult files from catalogs |
t5de-revision |
product, revision, rating, files (the file list), parent (IMVU product ID or 0) |
none (exp absent) |
the hub vouches for a revision's files |
Catalog API (t5de-catalog)
Served at the catalog's public URL. CORS: the hub website's origin may call POST /api/v1/uploads.
GET /health |
{ok: true} |
POST /api/v1/uploads |
Authorization: Bearer <t5de-upload token>; multipart form: file (the product as a zip: Create Mode's .chkn, files at the root) and notes (text, required for a new revision of an existing product, optional for the first). The catalog validates the files (t5de-api pack rules: names, limits, index.xml with ProductRating GA/AP), stores each file by sha256, then registers the revision with the hub (below) and answers the hub's answer: 201 {product, revision}. Errors: {error, message}, 400 for bad files, 401 for a bad token, 502 if the hub refused or failed. |
GET /api/v1/files/<sha256> |
the file's bytes, Cache-Control: public, max-age=31536000, immutable and ETag = the hash. A file used only by adult revisions needs Authorization: Bearer <t5de-access token> with adult: true (else 401/403, and Cache-Control: private). 404 if the catalog doesn't have it. |
GET /api/v1/revisions/<product>/<revision> |
{product, revision, rating, files}, for revisions the catalog hosts |
Configuration (environment): CATALOG_ADDR (listen address, default 127.0.0.1:4102), CATALOG_DATA (data folder:
SQLite database and files), CATALOG_PUBLIC_URL, HUB_URL (the hub's API), CATALOG_ID and CATALOG_KEY (from
registering on the hub), WEB_ORIGIN (the hub website, for CORS).
Hub API
For catalogs (server to server)
Authorization: Catalog <catalog ID>:<key>. Only approved catalogs.
POST /api/v1/catalog/revisions |
{uploadToken, files, rating ("general" or "adult"), parent (IMVU product ID or 0), name (from index.xml, for a new product), notes} → 201 {product, revision}. The upload token must be for this catalog. Without product in the token, a new product is made (owner: the token's person, name from name). With it, a new revision of that product (the person must own it; notes required). A file list identical to the latest revision's is refused (409 unchanged). |
For people (website, signed in; the proxy with its bearer token)
POST /api/v1/catalogs |
register a catalog: {name, url, contact, visibility ("public"/"private"), membership ("open"/"approval"/"invite")} → 201 {catalog, key} (the key is shown once) |
GET /api/v1/catalogs |
(anyone) approved public catalogs; the person's own, any status |
POST /api/v1/products/upload |
{catalog, product?} → {token, url}: an upload token and the catalog's upload URL |
GET /api/v1/products?q=&creator= |
(anyone) products, newest first; adult ones only for people with an adult account |
GET /api/v1/products/:id |
(anyone, adult rule as above) {product, revisions, inLibrary?, mine?} |
PATCH /api/v1/products/:id |
(creator) {name?, description?, rating?}; description is HTML, cleaned by the allowlist; rating may go general → adult |
POST /api/v1/products/:id/revisions/:n/retract |
(creator) |
GET /api/v1/library |
{library: [{product, revision, stayOn}]}: the person's products, each with the revision to use |
PUT /api/v1/library/:id |
get a product (add it) |
DELETE /api/v1/library/:id |
remove it |
PATCH /api/v1/library/:id |
{stayOn: n or null} |
GET /api/v1/products/:id/files |
for the proxy: {product, revision, rating, parent, name, files, catalogs: [url], token} for the revision this person uses (their pin, else the latest that isn't retracted); token is the t5de-revision token |
POST /api/v1/access |
for the proxy: {token}, a t5de-access token (adult if any of the person's IMVU accounts counts as adult) |
The realtime connection also tells the proxy {"type": "library"} when the person's library changes.
Console (moderators)
GET /api/v1/console/catalogs, POST /api/v1/console/catalogs/:id/approve, .../reject.
The proxy
- Fetches the library at connect and on
librarymessages; for each product,GET /api/v1/products/:id/files, downloads missing files from a listed catalog (GET <catalog>/api/v1/files/<sha256>, with an access token for adult revisions), checks each sha256, and writes the product as a pack in its cache (%APPDATA%\T5DE\proxy\hub-products\<id>\:t5de-product.jsonwith the hub's name and rating, andfiles/). From there it's served like any pack. - Products others wear in a room that it doesn't have are fetched the same way, then given to the client.
- Library products appear in the client's inventory (see the proxy's README).