openvibers/openvibe.inventory Commits

Inventory: README sections and STATUS.json fields the docs-currency checker requires (#13)

OP OpenVibers committed 10 Oct 2026 · 426549732b · 2 files, +87 −0

Docs only. The network's docs-currency checker (OpenVibe.Contracts `scripts/docs-currency.js`) wants every service README to have Purpose, Owns, Does not own, Depends on, Capabilities, Acceptance, Security and Deploy, and STATUS.json to name its repository. This adds the missing ones, written from the code, the migrations and the Contracts manifest; existing sections are unchanged. The checker now reports the repo as current.

Changes

ddiff --git a/README.md b/README.md
iindex 4b3ec11..f1d41f3 100644
---- a/README.md
++++ b/README.md
@@@ -10,6 +10,57 @@ and tested; Live switches to it next.
One inventory for an OpenVibe account, the way Steam's works across games. Sites, games and apps give items for what
people do; people keep them in one place and wear them on every site that shows their kind.
++## Purpose
++
++OpenVibe.Inventory is the network-wide inventory for an OpenVibe account (ADR-054): one place where the items people
++earn or are given across every site, game and app are held. It registers item kinds (a contract plus a renderer per
++surface), the definitions an issuer publishes and the instances a person owns, and keeps what each person has equipped.
++Every movement is one append-only ledger row and one `inventory.*` event, written in the same transaction as the change.
++Nothing is sold, bought, traded or converted into OpenCoins or Vibes (ADR-054 §5).
++
++## Owns
++
++Its own PostgreSQL database (`ov_inventory`) and everything in it: the tables `inventory_kinds`, `inventory_definitions`,
++`inventory_definition_aliases`, `inventory_instances`, `inventory_grants`, `inventory_equipped` and `inventory_ledger`
++(see [migrations/](migrations/)), plus the SDK's `event_outbox` (events not yet sent) and `account_data_events` (applied
++export and deletion receipts). It is the authority for item kinds, definitions, instances, grant receipts, the equipped
++set and the ledger. Nothing written there is ever a credential, a key or a token.
++
++## Does not own
++
++- **Identity** is OpenVibe.Network's: it issues the `usr_` subjects, usernames and Network tokens, and resolves an
++ `@name` for the Workshop. Inventory stores only the subject.
++- **Files** are OpenVibe.Media's: a Workshop badge image is uploaded to this service's Media tenant and referenced by its
++ `med_` id; Inventory keeps only the id and the metadata, never the bytes.
++- **Event delivery** is OpenVibe.Events': the outbox relay publishes Inventory's events there and the two account
++ subscriptions are created there.
++- **OpenVibe.Live's own cosmetics tables** (`user_cosmetics`, `user_equipped`) stay Live's; the migration script only
++ reads them (ADR-054 §8).
++
++## Depends on
++
++- **OpenVibe.Network** — sign-in (OAuth2 authorization code + PKCE, client `inventory`), the JWKS signing key and
++ `GET /internal/identity/resolve` for `@name`: `OV_NETWORK_URL`, `OV_NETWORK_INTERNAL_URL`, `OV_OAUTH_CLIENT_ID`,
++ `OV_OAUTH_CLIENT_SECRET`.
++- **OpenVibe.Events** — the outbox relay publishes `inventory.*` and the account-export/deletion subscriptions are
++ created there: `EVENTS_URL`, `INVENTORY_EVENTS_SECRET`, `EVENTS_RELAY_INTERVAL_MS`.
++- **OpenVibe.Media** — Workshop image uploads to this service's tenant: `OV_MEDIA_INTERNAL_URL`,
++ `INVENTORY_MEDIA_APP_KEY`.
++- **PostgreSQL** (`DATABASE_URL` to serve, `DATABASE_DIRECT_URL` to migrate) and, optionally, **Valkey** for shared
++ per-caller limit counters (`VALKEY_URL`).
++- **Packages** — `openvibe-contracts` and `openvibe-sdk` (auth, db, events, account-data, limits, service, valkey),
++ `openvibe-shared` (shell, frame, legal, seo, serve, showcase, cache-policy, metrics, ready, release), plus Express,
++ helmet, ajv, busboy, pg/PGlite and iovalkey.
++
++## Capabilities
++
++Its manifest (openvibe-contracts) registers: `inventory.item.read`, `inventory.item.list`, `inventory.equip.manage`,
++`inventory.item.grant`, `inventory.item.consume`, `inventory.definition.manage` and `inventory.definition.review` (the
++staff review route).
++
++It calls, on other services: `events.event.publish` and `events.subscription.manage` (OpenVibe.Events; the outbox relay
++and the account subscriptions) and `identity.subject.resolve` (OpenVibe.Network; resolving a creator's `@name`).
++
## The model (ADR-054)
- **Kind** (`live.name_effect`, `live.hat`, …): what its items carry (a JSON Schema), where one is equipped (slots) and
@@@ -109,6 +160,41 @@ fnm exec --using=22 npm run dev # http://localhost:5030
Without `DATABASE_URL` development uses an embedded PGlite database in `data/pglite` (one process only). `npm run
test:pg` runs the same suite through PostgreSQL and PgBouncer (see [.github/workflows/ci.yml](.github/workflows/ci.yml)).
++## Acceptance
++
++`npm test` runs every [test/*.test.js](test/) in its own process ([test/run.js](test/run.js)) against temp PGlite
++databases and an in-process mock Network, so no test needs the network or a running site. `npm run test:pg` runs the same
++suite through PostgreSQL and PgBouncer. The main files:
++
++- [test/inventory-api.test.js](test/inventory-api.test.js) — boot seeds Live's five kinds, the Workshop badge and 70
++ definitions; a grant is namespaced, idempotent and capped (serials); attributes are validated against the kind;
++ equipping is one item per slot; public reads show owned items and the owner's own read every state; consume/revoke;
++ a presented token must hold `inventory.item.read`; no buy/sell/trade route or money column exists.
++- [test/workshop.test.js](test/workshop.test.js) — a badge is judged by its own bytes (square PNG/WebP, 64–512 px, at
++ most 200 KB), submitted in review, published or rejected by staff with a reason, given by `@name`, limited (5 in
++ review, 20 submissions and 100 gifts a day), and taken down with or without revoking every copy.
++- [test/grantors.test.js](test/grantors.test.js) — Live names Quest on its six reward items; a grantor grants that item
++ only (no edit, no revoke), and Live sets and clears the list.
++- [test/account-data.test.js](test/account-data.test.js) — account export and deletion over the signed loopback route:
++ the ledger rows stay without their subject, supply counts stay true, and a creator's badges pass to the kind, retired.
++- [test/import-live.test.js](test/import-live.test.js) — Live's cosmetics move row for row: the dry run, `--apply` (origin
++ `migrated`, unlock time kept, idempotent) and `--verify`.
++- [test/public-cors.test.js](test/public-cors.test.js) — the public reads answer every origin with `*` and no credentials;
++ every other route stays same-site. [test/caller-limits.test.js](test/caller-limits.test.js) — a caller past its limit
++ gets 429 problem+json with Retry-After, the declared budgets hold, and health/ready/metrics are never limited.
++- [test/auth-ops.test.js](test/auth-ops.test.js), [test/auth-jwks.test.js](test/auth-jwks.test.js) and
++ [test/security-session.test.js](test/security-session.test.js) — PKCE sign-in and truthful readiness; the Network JWKS
++ fetch, rotation and outage; only a real Network person token is a session.
++- [test/security-secrets.test.js](test/security-secrets.test.js), [test/no-internal-key.test.js](test/no-internal-key.test.js)
++ and [test/open-redirect.test.js](test/open-redirect.test.js) — the OAuth client secret never leaves in a response, the
++ database or a log; no `X-Internal-Key` path; `next=` never leaves the site.
++- [test/discovery.test.js](test/discovery.test.js), [test/layout.test.js](test/layout.test.js) and
++ [test/asset-cache.test.js](test/asset-cache.test.js) — robots/sitemap/llms and JSON-LD, the server-rendered shell and
++ the boost marker, and the asset cache headers.
++- [test/nginx-auth-limit.test.js](test/nginx-auth-limit.test.js), [test/service-kit.test.js](test/service-kit.test.js) and
++ [test/perf-budget.test.js](test/perf-budget.test.js) — the nginx `/auth/me` zone, the graceful stop order, and the home
++ page's size budgets.
++
## Deploy (for the lead)
- **Deploy:** `sudo ovhost deploy inventory` on the host (git checkout at `/opt/inventory.openvibe.network`, unit
ddiff --git a/STATUS.json b/STATUS.json
iindex 822b414..d3feb1a 100644
---- a/STATUS.json
++++ b/STATUS.json
@@@ -1,4 +1,5 @@
{
++ "repository": "OpenVibers/OpenVibe.Inventory",
"stage": "alpha",
"code": true,
"wave": "W20",