Files
t5de-hub/docs/imports.md
T
dhkatz ed3a188b82
Release / release (push) Successful in 5s
CI / test (push) Successful in 1m4s
CI / image (apps/api/Dockerfile, t5de-hub-api) (push) Successful in 6s
CI / image (apps/web/Dockerfile, t5de-hub-web) (push) Successful in 21s
docs: permissions, console and imported IMVU products (#6)
Design for the next phases (discussed 2026-11-04):

- `docs/console.md` (new): permissions (`resource:action`), roles as bundles given at a scope (hub, catalog, product, room), one console for everyone who controls something, a tagged audit log scoped by permission, cases (reports, appeals, reviews, catalog deletion), notifications, API tokens, two-factor sign-in for sensitive permissions, "why can't I" and view-as, charts.
- `docs/imports.md` (new): catalogs importing IMVU products with the creator's grant (whole shop or chosen products), admin-recorded grants, requests, unclaimed creators, the job queue, `t5de-importer`, withdrawal, the spike.
- `docs/products.md`: drafts, unlisted products, membership enforced, catalog management routes, moderator edits, info history and locks, file blocks, the catalogs' changes feed, mirroring, the `t5de-import` token and importer routes.
- `docs/design.md`: pointers, phases and the later list.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Design for the next phases (discussed 2026-11-04):

- `docs/console.md` (new): permissions (`resource:action`), roles as bundles given at a scope (hub, catalog, product, room), one console for everyone who controls something, a tagged audit log scoped by permission, cases (reports, appeals, reviews, catalog deletion), notifications, API tokens, two-factor sign-in for sensitive permissions, "why can't I" and view-as, charts.
- `docs/imports.md` (new): catalogs importing IMVU products with the creator's grant (whole shop or chosen products), admin-recorded grants, requests, unclaimed creators, the job queue, `t5de-importer`, withdrawal, the spike.
- `docs/products.md`: drafts, unlisted products, membership enforced, catalog management routes, moderator edits, info history and locks, file blocks, the catalogs' changes feed, mirroring, the `t5de-import` token and importer routes.
- `docs/design.md`: pointers, phases and the later list.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: dhkatz <[email protected]>
2026-11-05 13:05:58 +00:00

11 KiB

Imported IMVU products

Creators who sell on IMVU can let T5DE users wear their IMVU products for free: a catalog imports the products from IMVU, hosts them as ordinary T5 products, and serves them to its members. Decided 2026-11-04 (the user's discussion). The contract with catalogs and proxies is in products.md; permissions and the console are in console.md.

Why the catalog imports

  • Responsibility stays with catalogs, as for uploads: the catalog that imports a product stores and serves its files, and its takedown contact answers for them. End users only download from catalogs, as for any T5 product.
  • Nothing changes for proxies: an imported product is a hub product with revisions. No aliasing of IMVU IDs, no re-signed IMVU requests.
  • Only consenting creators: nothing is imported without a grant from the creator (or one an admin records on their behalf, with evidence).

Words

  • Grant: a creator's consent for one catalog to import their products: the whole shop, or listed products.
  • Importer (t5de-importer): a program that fetches products from IMVU and uploads them to its catalog. Run by the catalog's operator beside an ordinary t5de-catalog. T5DE runs one for its own import catalog; any catalog may enable imports.
  • Import job: one product to import or re-check, queued at the hub.
  • Request: someone asking for a creator's product, or whole shop, to be on T5DE.
  • Unclaimed creator: a placeholder creator for IMVU creators without a hub account, whose consent an admin recorded. Claimed when they join and link that IMVU account.

What's imported

  • The product's own files: its CFL's entries, with the 16x16 placeholder textures replaced by the full-size originals its _contents.json lists (fetched from IMVU's CDN beside the CFL), and without _contents.json (packs don't use it). Its index.xml keeps its __DATAIMPORT of the parent.
  • The parent stays on IMVU (Decided, for now): the revision's parent is the IMVU product it derives from, as for uploaded products, and the client loads it from IMVU as it does for anyone's outfit. Option B (Decided): any product the creator made may be imported, whatever it derives from; deriving from a derivable product is what IMVU's derive permission allows. If keeping the parent on IMVU becomes a problem, imports can be flattened later (the whole chain in one product, parent 0).
  • Info: name and rating from IMVU's public API (api.imvu.com/product/product-<id>: product_name, rating, creator_cid), the higher of IMVU's rating and the files' ProductRating; a description saying it's imported with the creator's permission, linking its IMVU page. Creators edit it afterwards like any product.
  • Revisions: each IMVU revision imported becomes a hub revision ("Imported from IMVU revision 12"). Revision history and staying on a revision work as for any product.
  • One hub product per IMVU product (product.imvu_product, unique). Two catalogs importing the same product mirror the same revisions.

Grants

import_grant(id, catalog, creator person, imvu_id, scope ("shop" | "products"), products bigint[], exclude bigint[],
             listed, consent_version, consented_at, consent_kind ("verified" | "recorded"), evidence, recorded_by,
             proof_checked_at, accepted_at, accepted_by, withdrawn_at, status ("pending" | "active" | "suspended" | "withdrawn"))
  • Made by the creator: on the hub website, "Bring my IMVU shop": choose one of their linked IMVU accounts, a catalog with imports on, the scope (whole shop with exclusions, or listed products), listed or unlisted, and agree to the consent text. The text is versioned; the version and time are stored.
  • Accepted by the catalog: the catalog's team (import:accept) accepts or declines it, so nobody pushes imports onto a catalog without its operator agreeing. Until then it's pending.
  • Recorded by an admin (import:grant-on-behalf, hub-wide; for creators who won't make an account): the IMVU creator (ID or name), scope, catalog, and evidence (required: how they agreed, e.g. "Discord DM, 2026-11-04", optionally a screenshot). Optional proof without an account: the console gives a code to send the creator; they put it in their IMVU tagline, and the hub checks it as for linking. Shown as "consent verified" or "consent recorded by admin". The products belong to an unclaimed creator.
  • Scope "shop": any product whose creator_cid is the grant's IMVU account, including new ones, except exclusions. Scope "products": only those listed; the creator can add more (requests show as suggestions).
  • Changes: the creator (or an admin, for recorded grants) changes the scope, list and exclusions any time. Removing a product from a grant withdraws it (below).
  • Ending: the creator withdraws the grant, or the catalog's team ends it, or the IMVU account's link moves to another person (the grant is suspended until the new owner consents again), or the account is unlinked (suspended). Admins can withdraw any grant.

Unclaimed creators and claiming

An unclaimed creator is a hub person without credentials (can't sign in), named after the IMVU account ("Bar, on IMVU"), marked unclaimed. When someone links that IMVU account, the hub offers them the claim: the products, grants, requests and stats move to them, and the placeholder is deleted. Until then, withdrawal goes through admins (contacting T5DE, or the catalog's takedown contact).

Requests

import_request(person, imvu_creator, imvu_product (0: the whole shop), created_at)   -- unique per person and target
  • Anyone signed in can request a product (by IMVU ID or from its shop page in the client) or a creator's whole shop. At most 50 a day per person; one per person and target, so counts can't be inflated.
  • The hub looks up the product's creator (public API, cached in imvu_product_info).
  • Covered by an active grant: queued for import at once; the person gets the product in their library when it's done (pending until then; nothing on screen waits for it).
  • Not covered, grant by products: a suggestion for the creator ("12 people asked for X"), added with one click.
  • No grant: counted. The creator sees the demand when they bring their shop; the hub's creator page for a shop that hasn't joined can show "requested by 47 people". The hub never contacts creators through IMVU.
  • Admins see request counts per IMVU creator in the console (whom it's worth asking).

Import jobs

import_job(id, catalog, grant, imvu_product, kind ("import" | "recheck"), status ("queued" | "claimed" | "done" | "failed"),
           imvu_revision, reason, attempts, claimed_at, next_at, created_at, updated_at)
  • Queued by the hub: a request covered by a grant, a product added to a grant, a "shop" grant accepted (the products people requested so far, and all the shop's products if a way to list them is found), and a daily recheck of each imported product.
  • The hub checks IMVU's public API before queuing and at each recheck (rate-limited, as for parents): the product exists, its creator is the grant's account, its rating. Gone from IMVU: withdrawn. Rating changed to AP: adult at once. Retired (is_purchasable false): kept unless the creator excludes it.
  • The importer claims jobs, fetches, converts, uploads with a t5de-import token (products.md), and reports the outcome. Rechecks probe IMVU's CDN for revisions after the last imported one; a new one is imported.
  • Failures retry with backoff (1 min, 10 min, 1 h, then daily; failed for good after 5); the reason is shown in the console. The catalog's team can retry or re-import one product, and pause the importer (import:pause): the hub hands out no jobs while paused.

Withdrawal

A withdrawn imported product (grant withdrawn or narrowed, gone from IMVU, account link moved) is removed as moderation removes products: it leaves libraries, outfits and rooms; its page says it was withdrawn; the catalog deletes its files (the catalog changes feed, products.md). Unlike deleting an uploaded product, people who had it don't keep it: the consent is gone.

t5de-importer

A Go program (its own repository) beside a t5de-catalog; it stores nothing lasting but its working files.

  • Configuration: HUB_URL, CATALOG_ID, IMPORTER_KEY (from enabling imports on the catalog), IMPORTER_RATE (requests a second to IMVU, default 2), optionally an IMVU account for exact revisions (IMVU_USERNAME, IMVU_PASSWORD; the operator's choice and risk; the hub never holds IMVU credentials).
  • Revisions: without an IMVU account, the CDN is probed (HEAD productdata/<id>/<n>) once per product; afterwards a recheck tries the next few numbers. With one, authorizeProducts gives exact revisions.
  • Reads CFLs with t5de-api's cfl package; the conversion follows @imvu/chkn (dhkatz/imvu.js) as a reference.
  • Validates with t5de-api's pack rules before uploading; a product breaking them fails with the reason. (The spike checks whether typical IMVU products fit; imports may need their own limits.)

Hub routes

GET /api/v1/imports/catalogs approved catalogs with imports on, accepting grants
POST /api/v1/imports/grants {catalog, imvuId, scope, products?, exclude?, listed, consentVersion} → 201 {grant} (pending). With onBehalf: {imvuId, evidence, proof?} instead of a linked imvuId: an admin-recorded grant.
GET /api/v1/imports/grants the person's grants; with catalog=, a catalog's (its team)
PATCH /api/v1/imports/grants/:id {scope?, products?, exclude?, listed?}
POST /api/v1/imports/grants/:id/accept, .../decline the catalog's team
DELETE /api/v1/imports/grants/:id withdraw
POST /api/v1/imports/proofs (admins) {imvuId} → {proof, code}; POST .../proofs/:id/check
POST /api/v1/imports/requests {imvuProduct} or {imvuCreator} → {status: "queued" | "suggested" | "counted", product?}
GET /api/v1/imports/requests?imvuCreator= counts (the creator, admins)
GET /api/v1/imports/available?imvuProducts=a,b for the proxy's shop button: which can the person get (and the hub product ID), already have, or request
GET /api/v1/imports/jobs?catalog=&status= the catalog's team: the queue; POST .../jobs/:id/retry
POST /api/v1/imports/claim {imvuId}: claim an unclaimed creator's products for a linked account

For importers (Authorization: Importer <catalog ID>:<key>) in products.md.

Spike (before building the importer)

  1. Import one product by hand as a local pack (full-size textures, no _contents.json, parent kept). Does the client wear it as a T5 product, with the parent loading from IMVU? (Never committed: it's IMVU's files.)
  2. Do typical IMVU products fit the pack limits?
  3. How many HEAD requests does probing take for real shops; does the CDN limit them?
  4. Is there a public way to list a creator's products (for importing a whole shop at once)?

Later

  • Flattened imports (parent included), if keeping the parent on IMVU causes problems.
  • A dry run: preview an import (and anything over the pack limits) before it's published.
  • "Get on T5DE" on IMVU's shop and product pages in the client (a t5de patch, using /api/v1/imports/available).
  • Creators who don't want their products as parents of imports (an opt-out hiding imports that derive from theirs).