openvibers/openvibe.contracts AGENTS.md
AGENTS.md
| 1 | # AGENTS.md — OpenVibe.Contracts |
| 2 | |
| 3 | ## Project overview |
| 4 | |
| 5 | The machine-readable contract layer of the whole OpenVibe network: schemas, ids, the HTTP/error |
| 6 | envelope, capability checks, service/namespace/topic manifests, the staff capability map and the |
| 7 | compatibility aliases. A **library**, not a service: no runtime, no database, no ports, no deploy. |
| 8 | |
| 9 | - Package `openvibe-contracts`, `main` [index.js](index.js), version in [package.json](package.json) |
| 10 | (currently `0.410.0`, released as the git tag `v0.410.0`). |
| 11 | - Every service pins a release tarball and verifies service/app tokens with `serviceAuth` in |
| 12 | production; the service repositories run `openvibe-contracts-check` and the pin-drift check in CI. |
| 13 | - Scale at this release: 1261 schemas in [contracts/catalog.json](contracts/catalog.json), 67 service |
| 14 | manifests, 523 capability manifests, 14 namespace manifests and the staff map. |
| 15 | |
| 16 | ## What it owns |
| 17 | |
| 18 | - Contract IDs, semantic versions, owners, visibility and deprecation state. |
| 19 | - `SubjectRef` (`user|guest|service|app|mod|system` + stable id) and the legacy identity map shape. |
| 20 | - The request/error envelope: `traceparent`, `X-OpenVibe-Request-Id`, `Idempotency-Key`, RFC 9457 |
| 21 | problem details, resource `revision`/ETag. |
| 22 | - The capability contract (id, owner, input/output schema, permissions, resource constraints, quota |
| 23 | class, events), namespace and topic manifests, and compatibility aliases/deprecations. |
| 24 | - Service manifests (what each service runs, its routes and events) and the staff capability map. |
| 25 | - Generated JSON Schema, TypeScript and OpenAPI artifacts, and the estate map. |
| 26 | |
| 27 | Not owned: any runtime behaviour, a database, identity issuance (Network), or the implementation of a |
| 28 | capability (the owning service). |
| 29 | |
| 30 | ## How a service consumes it |
| 31 | |
| 32 | ```js |
| 33 | const contracts = require('openvibe-contracts'); |
| 34 | contracts.validate('identity.subject-ref@1', { type: 'user', id: 'usr_…' }); // { valid, errors } |
| 35 | contracts.ids.newId('user'); // usr_<ULID> |
| 36 | contracts.http.problem(403, 'capability.denied'); // RFC 9457 body |
| 37 | contracts.capabilities.check(claims, 'media.object.read', { namespace: 'live' }); |
| 38 | contracts.resource.parse('ovrn:media:prj_…:object/med_…'); |
| 39 | ``` |
| 40 | |
| 41 | `serviceAuth.requireCapability(...)` judges a request that presents a Bearer token **on that token |
| 42 | alone**; a bad token is never rescued by a legacy header. It verifies RS256, issuer, audience and |
| 43 | expiry, and grants a capability only by exact id or a `family.*` grant. Production services must use it. |
| 44 | |
| 45 | ## Repository map |
| 46 | |
| 47 | - **Public API:** [index.js](index.js) — `catalog`, `resolve`, `validate`, `assertValid`, `schema`, |
| 48 | `ids`, `http`, `capabilities`, `services`, `products`, `serviceAuth`, `modules`, `tools`, `zones`, |
| 49 | `usage`, `resources`, `staff`, `openapi`. |
| 50 | - **Implementation:** [lib/](lib/) — `registry.js` (catalog loading/resolution), `ids.js`, `http.js`, |
| 51 | `capabilities.js`, `services.js`, `products.js`, `service-auth.js`, `modules.js`, `tools.js`, |
| 52 | `zones.js`, `usage.js`, `resources.js`, `staff.js`, `openapi.js`, `money-reasons.js`. |
| 53 | - **Schemas:** [contracts/](contracts/) grouped by area (`actor/`, `agent/`, `ai/`, `bench/`, `billing/`, |
| 54 | `blog/`, …), indexed by [contracts/catalog.json](contracts/catalog.json). |
| 55 | - **Fixtures:** [fixtures/](fixtures/) — each contract has `valid/` and `invalid/` examples. |
| 56 | - **Manifests:** [manifests/](manifests/) — services, capabilities, namespaces, topics, policy |
| 57 | (`manifests/policy/staff-roles.json` is the one source for staff gates). |
| 58 | - **Compatibility:** [compatibility/](compatibility/) — `aliases.json`, `deprecations.json`, |
| 59 | `corrections.json`, `capability-schema-gaps.json`, `compat.js`. |
| 60 | - **Generated:** [generated/](generated/) — `json-schema/`, `typescript/`, `openapi/`; never hand-edit. |
| 61 | - **Scripts:** [scripts/generate.js](scripts/generate.js), [scripts/compat.js](scripts/compat.js), |
| 62 | [scripts/estate.js](scripts/estate.js), [scripts/docs-currency.js](scripts/docs-currency.js). |
| 63 | - **Service check:** [bin/check-service.js](bin/check-service.js) → `openvibe-contracts-check`. |
| 64 | - **Governance/ADRs:** [governance/](governance/), [docs/adr/](docs/adr/), [docs/](docs/). |
| 65 | |
| 66 | ## Commands |
| 67 | |
| 68 | ```bash |
| 69 | npm ci |
| 70 | npm test # every check in test/ (schemas, fixtures, manifests, compatibility, docs) |
| 71 | npm run generate # regenerate generated/json-schema, generated/typescript, generated/openapi |
| 72 | npm run estate # regenerate docs/ESTATE.md and manifests/repositories/ |
| 73 | node scripts/estate.js --check # fail on drift against the estate checkouts |
| 74 | node scripts/docs-currency.js <root> # check every checkout's README/STATUS against its code |
| 75 | node --check <file.js> |
| 76 | ``` |
| 77 | |
| 78 | The repository is consumed, never started; there is nothing to `npm start`. |
| 79 | |
| 80 | ## Adding or changing a contract (the release round) |
| 81 | |
| 82 | 1. Add the schema under the owning area in [contracts/](contracts/) with its `$id`, version, owner and |
| 83 | `@1`/next-version id, and add it to [contracts/catalog.json](contracts/catalog.json). |
| 84 | 2. Add [fixtures/<id>/valid/](fixtures/) and `invalid/` examples; the suite validates both. |
| 85 | 3. Add or update the capability/namespace/service manifest if the change introduces a capability. |
| 86 | 4. `npm run generate` and commit the regenerated files together with the schema (never by hand). |
| 87 | 5. Raise `package.json`'s version above main's newest version, add the CHANGELOG entry, commit and tag |
| 88 | `vX.Y.Z`. `scripts/compat.js` bases its comparison on `git describe HEAD^`, so a release needs its |
| 89 | **own commit after the merge** — do not tag a merge commit that main has moved past. |
| 90 | 6. Consumers bump their pinned tarball tag; a new capability id must ship here **before** the service |
| 91 | that uses it, or the service refuses every caller (unknown ids fail closed). |
| 92 | |
| 93 | ## Versioning and compatibility |
| 94 | |
| 95 | - Schemas and capabilities are semver'd: additive fields/ids are a minor, a breaking shape is a new |
| 96 | `@2` contract (never an edit to `@1`), with a migration path and a deprecation entry. |
| 97 | - [compatibility/](compatibility/) records aliases, deprecations and corrections; the suite checks them |
| 98 | against the catalog. |
| 99 | - A released tag is immutable: do not rewrite or re-tag it, and do not edit a released schema in place. |
| 100 | |
| 101 | ## Security |
| 102 | |
| 103 | - `serviceAuth` verifies the token alone (no legacy header rescue), RS256, issuer, audience and expiry; |
| 104 | capabilities are exact-id or `family.*`. |
| 105 | - The staff capability map ([manifests/policy/staff-roles.json](manifests/policy/staff-roles.json)) is |
| 106 | the single source for staff gates; do not duplicate it in a service. |
| 107 | - Schemas carry no secrets and no personal data; fixtures use fake values, and gitleaks scans history |
| 108 | in CI. Dependencies are `ajv` and `ajv-formats` only — adding one needs a reason. |
| 109 | |
| 110 | ## Tests |
| 111 | |
| 112 | `npm test` runs [test/run.js](test/run.js), which loads every `test/*.test.js` check (the run aggregates |
| 113 | sub-scripts for schema validation, fixtures, capability schemas, manifests, compatibility, changelog |
| 114 | history, docs currency, estate). It is plain Node `assert`, no framework. A check that cannot run |
| 115 | prints `<label>: skipped (<why>)`; `--strict` (or `OV_TEST_STRICT=1`) makes a skip fail. `npm run |
| 116 | estate` and `node scripts/docs-currency.js <root>` are separate drift checks used in CI. `ov test` runs |
| 117 | the harness's shard. |
| 118 | |
| 119 | ## Pitfalls |
| 120 | |
| 121 | - **Never hand-edit [generated/](generated/).** Run `npm run generate`; a generated diff you cannot |
| 122 | reproduce is a bug. |
| 123 | - **Every schema needs fixtures and a catalog entry.** A schema outside `catalog.json` does not exist to |
| 124 | consumers; a fixture-less schema is untested. |
| 125 | - **Do not reuse or renumber an id.** Consumers key on the exact id and version; a change is a new |
| 126 | version, a deprecation and an alias where needed. |
| 127 | - **Do not tag before the release commit.** `scripts/compat.js` looks at `HEAD^`; tag after the version |
| 128 | commit, and bump in the same commit as the generated output. |
| 129 | - **A capability must be released before a service guards a route with it**; an unknown id is refused, |
| 130 | never allowed. |
| 131 | - **`docs/ESTATE.md` and `manifests/repositories/` are generated from the estate checkouts**; run |
| 132 | `npm run estate` and commit the result, or `--check` fails. |
| 133 | - **`scripts/docs-currency.js` trusts the checkouts it is given**; run it with the estate root, not a |
| 134 | single repository, when checking a pin round. |
| 135 | - **`serviceAuth` is deliberately strict.** Do not add a compatibility shim that accepts an unsigned or |
| 136 | wrong-audience token to make an old caller work. |
| 137 | |
| 138 | ## Documentation |
| 139 | |
| 140 | - [README.md](README.md) — the API, contract groups, versioning and purpose. |
| 141 | - [docs/ESTATE.md](docs/ESTATE.md) — the generated estate map. |
| 142 | - [docs/adr/](docs/adr/) — every architecture decision (ADR-012 money, ADR-019 publishing, ADR-035 |
| 143 | data, ADR-043 Bot, ADR-044 Actor, ADR-050 Codes, ADR-057 Connect, …). |
| 144 | - [docs/sharing.md](docs/sharing.md), [docs/locker.md](docs/locker.md), |
| 145 | [docs/space-problem-codes.md](docs/space-problem-codes.md). |
| 146 | - [governance/](governance/) — contract governance process. |
| 147 |