openvibers/openvibe.contracts AGENTS.md

AGENTS.md

main 8.8 KB · 147 lines · markdown

1# AGENTS.md — OpenVibe.Contracts
2
3## Project overview
4
5The machine-readable contract layer of the whole OpenVibe network: schemas, ids, the HTTP/error
6envelope, capability checks, service/namespace/topic manifests, the staff capability map and the
7compatibility 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
27Not owned: any runtime behaviour, a database, identity issuance (Network), or the implementation of a
28capability (the owning service).
29
30## How a service consumes it
31
32```js
33const contracts = require('openvibe-contracts');
34contracts.validate('identity.subject-ref@1', { type: 'user', id: 'usr_…' }); // { valid, errors }
35contracts.ids.newId('user'); // usr_<ULID>
36contracts.http.problem(403, 'capability.denied'); // RFC 9457 body
37contracts.capabilities.check(claims, 'media.object.read', { namespace: 'live' });
38contracts.resource.parse('ovrn:media:prj_…:object/med_…');
39```
40
41`serviceAuth.requireCapability(...)` judges a request that presents a Bearer token **on that token
42alone**; a bad token is never rescued by a legacy header. It verifies RS256, issuer, audience and
43expiry, 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
69npm ci
70npm test # every check in test/ (schemas, fixtures, manifests, compatibility, docs)
71npm run generate # regenerate generated/json-schema, generated/typescript, generated/openapi
72npm run estate # regenerate docs/ESTATE.md and manifests/repositories/
73node scripts/estate.js --check # fail on drift against the estate checkouts
74node scripts/docs-currency.js <root> # check every checkout's README/STATUS against its code
75node --check <file.js>
76```
77
78The repository is consumed, never started; there is nothing to `npm start`.
79
80## Adding or changing a contract (the release round)
81
821. 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).
842. Add [fixtures/&lt;id&gt;/valid/](fixtures/) and `invalid/` examples; the suite validates both.
853. Add or update the capability/namespace/service manifest if the change introduces a capability.
864. `npm run generate` and commit the regenerated files together with the schema (never by hand).
875. 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.
906. 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
113sub-scripts for schema validation, fixtures, capability schemas, manifests, compatibility, changelog
114history, docs currency, estate). It is plain Node `assert`, no framework. A check that cannot run
115prints `<label>: skipped (<why>)`; `--strict` (or `OV_TEST_STRICT=1`) makes a skip fail. `npm run
116estate` and `node scripts/docs-currency.js <root>` are separate drift checks used in CI. `ov test` runs
117the 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