openvibers/openvibe.services AGENTS.md

AGENTS.md

main 8.2 KB · 132 lines · markdown

1# AGENTS.md — OpenVibe.Services
2
3## Project overview
4
5OpenVibe.Services is the network's developer platform and resource control plane, served at
6`https://openvibe.services`. It is one console and API over OpenVibe.Network's projects, apps,
7credentials, grants and quotas; it also owns release metadata, the authority registry and the merged
8resource index, and (step 10) control operations routed to the authority that owns a resource.
9
10- **Entry:** `server/index.js` (`node server/index.js`); Express app in `server/app.js`.
11- **Port / service id:** `127.0.0.1:4930`, id `services`; unit `openvibe-services.service`.
12- **Portal:** `server/http/portal.js` (`/projects`, `/projects/:project/resources`, `/secrets`,
13 `/usage`, `/export`, `/apps/:app`), `server/http/mod.js`, `server/http/tools.js`.
14- **API and machine routes:** `server/http/api.js` (`/api/v1`), `server/http/openapi.js`,
15 `server/http/network-mcp.js` (`/mcp`), `server/http/discovery.js` (robots/sitemap/llms).
16- **Generated docs:** `server/docs/`, `server/http/docs.js`, `server/http/ai-docs.js`,
17 `server/http/money-docs.js` — generated from the pinned contracts, capability catalog, SDK
18 `.d.ts` files and ADRs at boot; never hand-written where it can drift.
19- **Resource index / control:** `server/resources.js`, `server/authorities/index.js`,
20 `server/registry/`, `server/domain/`; SDK `openvibe-sdk/resources` does the fan-out and merge.
21- **Pages:** server-rendered through `openvibe-shared/shell`; the only own scripts are the optional
22 in-browser webhook verifier and a copy button. No SPA, no build step.
23
24## Owns, does not own
25
26Owns: release metadata keyed to Network app ids (validated `services.app-manifest@1` and
27`mods.mod-manifest@1`; releases `draft → published → deprecated → revoked` and an append-only log);
28the merged resource index (`services.resource.read`, read live from every authority, never stored as
29truth); trust tiers (metadata only); playground run logs; the events `services.app.published`,
30`services.app.deprecated`, `services.app.revoked` and `services.moderation.action`; the generated
31reference and the console pages.
32
33Does not own: projects, members, apps, credentials, grants, allowances, quotas or audit — those are
34OpenVibe.Network (`/api/v1/projects`), and Services never copies them. Secrets are Network's:
35Services renders a secret only once, never stores, logs or re-displays it. Identity, billing and
36moderation stay with their services.
37
38## Who calls it, what it calls
39
40Developers and first-party services use the console and the public API; SDK
41`openvibe-sdk/projects` and `openvibe-sdk/resources` are the client surface, and the MCP server at
42`/mcp` exposes the network's read-only tools. Services calls:
43
44- **Network** — SSO (`services`, PKCE S256), JWKS, `/api/v1/projects` with the person's own access
45 token, the registry, client-credentials tokens, and `/internal/confirmations` for sensitive
46 control operations.
47- **Events** — the outbox relay publishes `services.app.*` with Services' service token; playgrounds
48 publish with the app's own token; the project archive reads app events with an export token.
49- **Media** — playground uploads with the app's token; the project archive lists objects and
50 namespaces with an export token.
51- **Authorities** — the registry is built at boot from the pinned `openvibe-contracts` service
52 manifests (every service whose manifest lists `<id>.resource.read`); the loopback origin comes from
53 the manifest, `SERVICES_<ID>_URL` overrides it. No polling loop: each request reads the
54 authorities.
55
56The authority registry is not hand-maintained: turning a capability on in a service's manifest puts
57it in the registry on the next boot.
58
59## Database and migrations
60
61PostgreSQL `ov_services` (ADR-035) via `openvibe-sdk/db`: `DATABASE_URL` (PgBouncer) for the app,
62`DATABASE_DIRECT_URL` for migrations. `server/db.js` applies `migrations/NNNN_*.sql` at boot under an
63advisory lock, then serves with the pooled role; a held migration is logged and does not crash the
64service. Never edit a migration that has run; a new table/column is an expand migration and removal
65waits for a contract migration. Valkey is optional and holds only per-actor limit counters.
66`telemetry_samples` is this service's own table, pruned after 14 days.
67
68## Auth
69
70Network RS256 JWTs (JWKS) plus the SSO session cookie. Service tokens carry `openvibe.services`
71audience and the route's capability (`services.release.read`, `services.release.manage`,
72`services.resource.read`, `services.resource.control`); a person's user token is used only for the
73person's own projects and the pages. The index answers a first-party service token, or one project
74at a time for a person who owns or belongs to it — never unscoped, and a non-member project is the
75same 404 Network uses. `/api/v1/authorities` is first-party loopback data. Sensitive control
76operations require Network's confirmation; until the grant exists they fail with
77`confirmations.unavailable` and nothing is applied. No path sends or accepts a shared loopback key.
78
79## Deploy
80
81`sudo ovhost deploy services` (git-checkout: fetch, fast-forward `/opt/openvibe.services`, install on
82a lockfile change, restart, wait for `/api/ready`; rollback `sudo ovhost rollback services --to <sha>`).
83Unit `deploy/systemd/openvibe-services.service`, env `/etc/openvibe/services.env`, nginx
84`deploy/nginx/openvibe.services.conf`, database `ov_services`. The release migrates at boot. A
85rollback across the console migration serves the old index and the console pages answer 404 until
86the next deploy.
87
88## Commands and tests
89
90```bash
91npm install
92npm run dev # http://localhost:4930
93npm test # PGlite, in-process stub Network/Events/Media
94npm run test:pg # the same on the PostgreSQL containers
95npm test -- index scaffold # only matching files
96npx openvibe-contracts-check --service services --src server
97```
98
99`test/run.js` uses `openvibe-shared/test-runner` (180 s, serial); each file gets a temp database and
100stubs, never the network. The suite covers the crawl artifacts and their `lastmod` source, the
101ADR-013 trust-tier migration, secrets never stored or re-displayed, honest Network errors, the scope
102editor, generated docs matching the pins, v2-only webhook verification, playground grants, usage and
103resource pages, project export/delete, the resource index auth matrix and cursors, readiness/metrics,
104and the home page's size budgets (`test/perf-budget.test.js`).
105
106## Conventions
107
108- CommonJS, 4-space indent, single quotes, semicolons. Server-rendered HTML; escape every value
109 (names, problem details, OVRNs) and keep pages usable without JavaScript.
110- RFC 9457 problem+json for API errors (`openvibe-contracts`); render Network's status/code/detail
111 and request id rather than inventing a friendlier error.
112- Guard routes with `openvibe-contracts` `requireCapability`; do not hand-roll a grant check.
113- Anything generated (docs, OpenAPI, manifests, sitemap/llms) is rendered from the pinned packages
114 and `STATUS.json`, with the versions shown; never patch the output by hand.
115- A release is validated against the pinned contracts before it is stored; publish/deprecate/revoke
116 are state transitions with an event, not direct row edits.
117
118## Key pitfalls
119
120- **Never persist or re-display a secret.** Network returns a client secret once; Services passes it
121 through and drops it. The export archive must contain no secret — tests assert both.
122- **A person's resource read is never unscoped.** `?project=` is required for a user token, Network
123 answers membership, and another project's rows must not appear even if an authority ignores
124 `?owner=`.
125- **Trust tiers are metadata**, not a grant check: changing a tier never widens access.
126- **The merged index is not stored as truth.** Read the authorities per request; a failed authority
127 is named in `stale`, never silently dropped or fatal.
128- **Control operations need the Network confirmation** and the authority's own capability; a gated
129 action without the grant fails closed.
130- **Trust the pinned versions:** docs, manifest validation and compatibility pages all come from
131 `openvibe-contracts`/`openvibe-sdk`; bump the pins instead of copying their content.
132