openvibers/openvibe.network AGENTS.md

AGENTS.md

main 14.2 KB · 184 lines · markdown

1# AGENTS.md — OpenVibe.Network
2
3## Project Overview
4
5OpenVibe.Network is the identity and account service of the OpenVibe network and its token issuer: accounts, sessions,
6anonymous identities, canonical subjects (`usr_…`/`gst_…`), OAuth2/OIDC, refresh tokens, service principals and their
7grants, developer projects/apps/quotas, notifications and email, themes and per-service overrides, user modules,
8platform blocks, the follow graph, presence, the Dock's state, creator analytics, the OpenCoins wallet, the machines
9hub, the Locker key directory, the service registry, the status page and the network changelog proxy.
10
11- **Domain:** `openvibe.network` · **Port:** 4000 · **Service id:** `network` · **Env prefix:** `NETWORK`/`OV_`
12- **Runtime:** Node 22, Express 4, PostgreSQL 18 through `openvibe-sdk/db` (PGlite in development/tests), Valkey 9,
13 RS256 signing key (`JWT_PRIVATE_KEY` or `data/keys`), email via Resend, optional Discord/GitHub.
14- **Read first:** [README.md](README.md), [docs/developer-projects.md](docs/developer-projects.md),
15 [docs/sign-in.md](docs/sign-in.md), [docs/dock.md](docs/dock.md), [docs/retirement.md](docs/retirement.md),
16 [docs/security/](docs/security/), [.env.example](.env.example).
17
18**Network context:** Network is the issuer every other service trusts. It signs `svc:network` tokens for OpenVibe.Events,
19OpenVibe.AI (site copy) and OpenVibe.Bot (machines). It does **not** own money (OpenVibe.Billing), a service's data
20(the user-module namespace belongs to its owner), the contracts it serves (OpenVibe.Contracts) or the Frame's code
21(OpenVibe.Shared). Services call its internal routes only with a capability-scoped service token; the old
22deployment-wide internal key was retired on 2026-09-29.
23
24## Commands
25
26```bash
27npm ci # install (contracts/sdk/shared pinned by tag)
28npm run dev # NODE_ENV=development node server/index.js, PGlite under data/pglite
29npm start # node server/index.js
30npm test # every test/*.test.js on temp databases, no network
31npm run test:pg # the same through PostgreSQL + PgBouncer
32npm test -- oauth # only test files whose name contains the word
33npm run reset-db # node server/reset-db.js (development)
34npm run grant-admin # node server/grant-admin.js
35node --check <file.js> # syntax check (no linter configured)
36```
37
38No build step for the server. Public pages are plain HTML/JS under [public/](public/) (`index.html`, `login.html`,
39`my.html`, `admin.html`, `wallet.html`, `pay.html`, assets and the brand). Production refuses to boot without
40PostgreSQL settings.
41
42## Architecture at a Glance
43
44- **Entry:** [server/index.js](server/index.js) — Express app, CORS, migrations, service/route init and periodic tasks;
45 `openvibe-network.socket` keeps connections queued across restarts.
46- **Config:** [server/config.js](server/config.js) — port 4000, JWT/DB/OAuth/connected services; production
47 `/etc/openvibe/network.env`. Only variable names appear in code/docs.
48- **Auth/OAuth/OIDC:** [server/auth/](server/auth/) — `routes.js` (register, login, profile, anon, multi-account,
49 follows), `oauth-routes.js` (authorize, token), `oidc.js` (discovery, id_token, userinfo), `refresh-tokens.js`
50 (SHA-256, rotating families), `session.js`, `fedcm.js`, `passkeys.js`, `mfa.js`, `qr-signin.js`, `sso-*.js`,
51 `revocation.js`, `reauth.js`, `staff-claims.js`.
52- **Identity/grants:** [server/identity/](server/identity/) — `principals.js` (seeded grants), `internal-routes.js`,
53 `account-data.js`, `emails.js`, `follows.js`, `blocks.js`, `adult.js`, `friends.js`, `grants-admin.js`,
54 `account-aliases.js`.
55- **Developer platform:** [server/developer/](server/developer/) — projects, members, apps, credentials, grants,
56 quotas, usage, audit; [docs/developer-projects.md](docs/developer-projects.md).
57- **Notifications:** [server/notifications/](server/notifications/) + [server/push/](server/push/) — CRUD,
58 preferences, quiet hours/digests, email queue, cross-service push, action center; [server/actions/](server/actions/).
59- **Themes/modules/dock/presence:** [server/themes/](server/themes/); user modules in
60 [server/identity/modules.js](server/identity/modules.js) and [server/identity/module-events.js](server/identity/module-events.js);
61 the Dock in [server/dock/](server/dock/), presence in [server/presence/](server/presence/), the home/menu feeds in
62 [server/home/](server/home/), [server/home-screen/](server/home-screen/), [server/menu-feeds/](server/menu-feeds/).
63- **Wallet/money:** [server/coins/](server/coins/), [server/wallet/](server/wallet/), [server/money/](server/money/) —
64 balance, history, and the idempotent `internal/coins/*` credit/debit/transfer.
65- **Machines/Locker/Connect:** [server/registry/machines.js](server/registry/machines.js),
66 [server/bot/](server/bot/), [server/link/](server/link/), [server/locker/](server/locker/),
67 [server/connect/](server/connect/), [server/consent/](server/consent/), [server/qr-signin/](server/qr-signin/).
68- **Analytics/profile/status/registry:** [server/analytics/](server/analytics/), [server/profile/public.js](server/profile/public.js),
69 [server/status/](server/status/), [server/registry/](server/registry/), [server/seo/](server/seo/),
70 [server/operator/](server/operator/), [server/history/](server/history/), [server/levels/](server/levels/).
71- **Admin/setup:** [server/admin/routes.js](server/admin/routes.js), [server/admin/setup.js](server/admin/setup.js),
72 [server/admin/ai-config-proxy.js](server/admin/ai-config-proxy.js); public [public/admin.html](public/admin.html).
73- **Database:** [server/db/](server/db/) and [migrations/](migrations/) (73 migrations; every table in the README's
74 Database Tables section is real): `users`, `oauth_clients`, `oauth_codes`, `refresh_tokens`, `anon_users`,
75 `user_sessions`, `themes`, `user_theme_prefs`, `site_settings`, `audit_log`, `notifications`,
76 `notification_preferences`, `follows`, `wallets`, `coin_transactions`, `dev_*`, `analytics_*`, `user_modules*`,
77 `network_event_outbox`, `user_blocks`, `user_follows`, `locker_keys*`, etc.
78- **Deploy:** [deploy/systemd/openvibe-network.service](deploy/systemd/openvibe-network.service) (+ socket/drop-in),
79 [deploy/scripts/deploy.sh](deploy/scripts/deploy.sh) runs `ovhost deploy network --install-units`; `sudo
80 /opt/openvibe.network/deploy/scripts/deploy.sh` (fast-forward, install on lockfile change, restart, `/api/ready`
81 poll, automatic rollback). Database `ov_network`; nginx in [deploy/nginx/](deploy/nginx/).
82
83## Ownership and Boundaries
84
85Network owns accounts, sessions, anonymous identities, canonical subjects and aliases, the OAuth clients/codes,
86refresh-token families, signing keys/JWKS, service principals and grants, mod principals, developer projects/apps/
87credentials/grants/quotas/usage/audit, notifications/email, themes/preferences, user modules and their revisions,
88platform blocks, the follow graph, creator analytics, presence, Dock state, the OpenCoins wallet/ledger, the machines
89hub, the Locker key directory (public keys and wrapped backups, opaque), the registry/status/changelog proxy and the
90public profile. It does **not** own money (Billing), streams/channel points (Live), any service's own rows, the
91contracts (Contracts) or the Frame (Shared).
92
93## Auth and Capabilities
94
95- Network is the issuer: RS256 tokens signed with its key, JWKS published, `token_valid_after` cutoffs so services
96 refuse older tokens. Refresh tokens are stored as SHA-256, rotate as a new generation of one `family_id`, and a
97 reused token (after a 10-second two-tab grace) revokes the family and audits `oauth_refresh_reuse`.
98- OIDC: discovery at `/.well-known/openid-configuration` and `/.well-known/oauth-authorization-server`; scope
99 `openid` adds an RS256 `id_token` (aud = client, nonce echoed, `sub` = Network id, `subject_id` = `usr_…`);
100 `/oauth/userinfo` returns the same.
101- Capabilities (audience `openvibe.network`): `identity.subject.resolve`, `mods.grant.manage`,
102 `network.account.deletion.confirm`, `network.account.export.contribute`, `network.analytics.creator.read`,
103 `network.blocks.read`, `network.coins.credit|debit|transfer`, `network.follows.read|write`,
104 `network.presence.read`, `network.integration.github.read`, `network.modules.read|write`,
105 `network.notifications.push`, `network.operator.alert`, `network.project.manage|read`, `network.secret.use`,
106 `network.staff.read`, `network.status.incident`; the hub's `network.machines.read|manage`. Grants other services
107 hold are seeded in [server/identity/principals.js](server/identity/principals.js).
108- CORS: only discovery and the registry answer any origin (`Access-Control-Allow-Origin: *`, preflight included);
109 every other route keeps the first-party allow-list ([server/public-cors.js](server/public-cors.js)).
110- Staff roles are claim-based (`admin`, `global_mod`, …); the admin panel/API gates on them. Never trust a role from a
111 request body; never accept a typed token where a session is required.
112- Redirects after sign-in (`/login?return=`, `/sso/fanout?next=`) never run `javascript:` or leave an owned host;
113 OAuth redirects must be one of the client's registered URIs.
114- Errors are structured; internal routes take a capability-scoped service token only. The shared internal key is
115 retired — do not reintroduce it.
116
117## API and Pages
118
119Pages: `/` (landing), `/login`, `/my` (account), `/@<username>` (public profile), `/wallet`, `/pay`, `/admin`
120(+ `#setup`, `#glossary`), `/admin/review`, `/status`, `/connect`, and the shared Frame/legal pages.
121API: `/api/auth/*`, `/oauth/*`, `/.well-known/*`, `/api/v1/{subjects,profiles,registry,projects,machines,presence,
122coins,themes,modules,blocks,follows,notifications,dock,agent-keys,...}`, `/internal/*` (capability tokens),
123`/contracts/*.json`, `/shared/*`, `/api/health`, `/api/ready`, `/release.json`, `/metrics`.
124
125## Conventions
126
127- CommonJS, 4-space indent, single quotes, semicolons; no linter/formatter. Async routes through
128 [server/async-routes.js](server/async-routes.js).
129- New schema = a new `migrations/NNNN_name.sql`; never edit one that has run. Services' rows are keyed by canonical
130 `usr_…`; an alias resolves to the canonical subject.
131- Session/auth changes are security changes: add a test in the matching security suite, not only the happy path.
132- Public responses carry explicit fields; never return a password hash, a refresh token, a client secret or another
133 person's email. A hidden profile shows only name and picture and is not indexed.
134- Analytics keep no personal data beyond a day's salted hash; each day's visitor salts are deleted after the rollup.
135- Notification events and the outbox are written in the change's transaction; a replay is one row.
136- CSP/first-paint: public pages are server-served HTML; no Font Awesome; phone first; "Calm reduces, only Off
137 removes".
138- Provider secrets come from the environment first and are never printed; only names appear in docs.
139- Admin/setup changes: the owner settings proxy writes a key once (fresh sign-in, the service's vault, audit, alert)
140 and never shows it again.
141
142## Key Pitfalls
143
144- **Refresh-token reuse revokes the family.** Keep the 10-second grace and the hashed storage; never log or return a
145 refresh token. A raw pre-change row is hashed on first use.
146- **Redirects and CORS:** a post-login target must be owned and http(s); a wildcard belongs only to discovery/registry.
147 Cross-site cookie writes must be refused; a Bearer is not a session.
148- **Subjects:** `GET /api/auth/me` returns only the caller's own `subject_id`; an account without one gets it minted
149 on first call. Other services ask via `identity.subject.resolve`.
150- **Account export/deletion (ADR-033):** every row type is in the export, and deletion sweeps every namespace,
151 including Locker keys, presence, Dock, modules, blocks, follows, analytics and outbox receipts; a redelivery changes
152 nothing. Account deletion needs re-authentication.
153- **Coins:** the wallet ledger is idempotent by key; OpenCoins are loyalty points, never money or Vibes. Never debit
154 without an idempotency key.
155- **Analytics privacy:** a raw event may be kept ≤30 days; a visitor is a day-salted hash only; no IP/user-agent in a
156 rollup.
157- **Machines:** pairing and choosing roles require the person's own session; a token acting for the person may only
158 read/rename/revoke. Bot is the registry underneath.
159- **Deploy blockers:** an older release starts with empty `frame_cache`/`frame_hits` (renamed from `chrome_*`), and
160 does not know `tool_domains` mirror rows. Record the rollback target with that in mind.
161- **Socket activation:** requests wait for the new process; do not replace the socket unit with a plain listen.
162
163## Testing
164
165`npm test` runs the files in [test/](test/) on temp databases with no network. They cover OAuth/PKCE, OIDC discovery,
166refresh rotation/reuse, SSO/FedCM, subjects/principals/grants, export/deletion, developer projects/apps/usage, user
167modules, blocks, follows, analytics privacy, the registry/exposure, notifications/events, status/incidents, operator
168alerts, actor limits, machines, presence, Dock, Locker keys, Connect, consent, QR sign-in, levels, the admin panel and
169the security suites (`security-session`, `security-redirects`, `security-secrets`, `security-private`,
170`security-idor`, `security-ssrf`), plus the deploy wrapper. `npm run test:pg` for PostgreSQL. Run the narrowest file
171first, then the suite; always `node --check` changed files.
172
173## Documentation
174
175- [README.md](README.md) — purpose, what it does, account ID, owns/does-not-own, capabilities, architecture, setup,
176 deploying, TLS, notifications, Dock, email, provider/project secrets, anonymous users, user modules, blocks,
177 follows, multi-account, OAuth2, observability, analytics, client libraries, admin panel, connected services,
178 database tables, acceptance, security, sharing.
179- [docs/developer-projects.md](docs/developer-projects.md), [docs/sign-in.md](docs/sign-in.md), [docs/dock.md](docs/dock.md),
180 [docs/retirement.md](docs/retirement.md), [docs/security/authenticator.md](docs/security/authenticator.md),
181 [docs/security/passkeys.md](docs/security/passkeys.md), [docs/slo.md](docs/slo.md),
182 [docs/platform-plan.md](docs/platform-plan.md), [docs/capabilities-proposal/](docs/capabilities-proposal/).
183- [SECURITY.md](SECURITY.md), [.env.example](.env.example).
184