Decorations over IMVU rooms: the hub's side
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]>
This commit is contained in:
dhkatzandClaude Opus 5.5 committed 2026-10-30 03:58:42 -07:00
1 parent 2a1fc94df6
commit f65f36c1ea
12 files changed
+2325 -6

No files matched your search

+16 -2
View File
@@ -11,7 +11,7 @@ cookies. T5DE's proxy calls it directly with `Authorization: Bearer <session tok
Errors are `{"error": "<code>", "message": "<text for people>"}` with a 4xx/5xx status; `message` can be shown as
is. Codes: `invalid`, `signed_out`, `forbidden`, `not_found`, `no_such_imvu_account`, `already_linked`,
`code_missing`, `expired`, `link_cap`, `rate_limited`, `imvu_unavailable`.
`code_missing`, `expired`, `link_cap`, `rate_limited`, `imvu_unavailable`, `unknown_product`, `hosted_room`.
## Accounts (Better Auth)
@@ -30,7 +30,7 @@ is. Codes: `invalid`, `signed_out`, `forbidden`, `not_found`, `no_such_imvu_acco
| `POST /api/auth/device/token` | `{grant_type: "urn:ietf:params:oauth:grant-type:device_code", device_code, client_id}` → `{access_token}` (a session token, used as a bearer token) or `{error: authorization_pending \| slow_down \| expired_token \| access_denied}` |
| `POST /api/v1/device/approve` | (website, signed in) `{code}`: signs the waiting proxy in as this person |
| `POST /api/v1/badges` | (bearer or website session) `{imvuIds}` (at most 200 IMVU account IDs) → `{badges}`: those linked to a T5DE account that turned on `showBadge` for them and isn't banned. Nothing else about them. 120 a minute per person. |
| `GET /api/v1/realtime` | WebSocket, bearer token: presence, outfits, the room channel and hosted rooms (`room.*`, docs/hosted-rooms.md). Protocol in `apps/api/src/realtime.ts`. |
| `GET /api/v1/realtime` | WebSocket, bearer token: presence, outfits, the room channel, decoration notices (`decor`) and hosted rooms (`room.*`, docs/hosted-rooms.md). Protocol in `apps/api/src/realtime.ts`. |
| `GET /.well-known/jwks.json` | the Ed25519 keys JWTs are signed with |
## Website routes (signed in unless noted)
@@ -67,6 +67,20 @@ the `GET`s also signed out.
A room is `{n, instanceId, chatId, name, description, roomPid, rating, maxUsers, ownerImvuId, ownerName, occupancy,
createdAt}`; `instanceId` is `2100000000-<n>`, `chatId` 2100000000 + n, `occupancy` the people in it now.
## Decorations
T5 products placed in ordinary IMVU rooms (docs/products.md, "Decorations"). `instance` is IMVU's room instance,
`<owner's customer ID>-<n>` (`^[1-9][0-9]{0,11}-[1-9][0-9]{0,9}$`); hosted rooms' (`2100000000-<n>`) are refused
(400 `hosted_room`: they have their own scene). A room's decorations are `{instance, contents, hash}`: `contents`
is a list of `[furniture instance ID (integer), hub product ID, placement (up to 200 characters)]`, `hash` the
sha256 (hex) of `contents` as JSON.
| | |
| --- | --- |
| `GET /api/v1/decorations/:instance` | (anyone, also signed out) the room's decorations as the viewer sees them: only products with a revision that isn't retracted, adult ones only for people with an adult account. A room without any: `contents: []`. |
| `GET /api/v1/decorations?instances=a,b` | (anyone) `{rooms: [{instance, contents, hash}]}`, up to 20 rooms, in the order asked |
| `PUT /api/v1/decorations/:instance` | (bearer or website session) `{contents}` → the room's decorations as the person sees them. Only if one of the person's linked IMVU accounts is the room's owner (else 403 `forbidden`). Up to 300 items, each furniture ID once; every product must be registered (400 `unknown_product`). An empty list removes them. Everyone with a chat in the room gets `{"type": "decor", "room": instance}` on the realtime connection. 60 a minute. |
## Products, catalogs and libraries
The routes and tokens are in docs/products.md. Products are `{id, name, description, rating, latest, creator,
+2 -2
View File
@@ -287,8 +287,8 @@ 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.
- **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
+17
View File
@@ -99,3 +99,20 @@ every 10 minutes, 20 at a time.
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).
## Decorations
T5 furniture and pose products placed in an ordinary IMVU room (design.md, "Decorations over IMVU rooms"). IMVU
keeps the room without them; the hub keeps them (`room_decoration`, routes in api.md), and only T5DE users see them.
- **Saving**: when the room's owner saves the room (`test.setRoomState`) or the client broadcasts a furniture change
in it, the proxy takes the hub products out before it goes to IMVU, and `PUT /api/v1/decorations/<instance>` the
room's full list of them (`[furniture instance ID, hub product ID, placement]`).
- **Loading**: when the client loads an IMVU room (`test.getRoomState`), the proxy `GET`s its decorations and adds
them to the room's contents (as local IDs, fetching the products as for outfits); the `hash` tells it whether
what it has is current. In the room, an incoming furniture broadcast from IMVU gets the decorations added back
the same way.
- **Changes**: after a `PUT`, every proxy with a chat in that room gets `{"type": "decor", "room": instance}` on the
realtime connection and `GET`s again.
- Viewers only get products they may see: registered, with a revision that isn't retracted, adult ones only for
adult accounts. Furniture instance IDs are the proxy's to choose; they must not clash with IMVU's in that room.