Bento docs
Architecture

Contracts

Anything that crosses a boundary, between modules, services, backends or the frontend, is specified in contracts/ as OpenAPI, JSON Schema or a short Markdown spec. No shared code crosses those boundaries, which is what lets the Go edge serve Nest services and a Python backend join later.

contracts/Checked against the current v1 readiness tracker, 16 October 2026

What is specified

203HTTP operations
118event schemas
45capabilities
3signed assertions
FolderSpecifiesChecked by
http/The public API as OpenAPI 3.1, and the generator for frontend and k6 types.Redocly lint, oasdiff, route parity
events/The event envelope and one JSON Schema per event type and version.Producers and consumers in both backends
capabilities/Request and response schemas for synchronous module-to-module calls.Catalog parity, focused backend tests and optional split end-to-end checks
auth/Principal, client-context and service assertions, with test vectors.Vectors in both backends
edge/The route listing the edge reads from each service.Split end-to-end suite
realtime/The WebSocket protocol, topics and rooms.Realtime contract test in CI
telemetry/Required resource attributes and what must never be recorded.Review; local telemetry check
ai/Task definitions and model pricing.Catalog parity in CI
audit/The hash chain, with vectors, and the export feed format.Audit end-to-end suite
privacy/The personal data inventory.Review when a module changes

HTTP API

contracts/http/openapi.yaml is the source of truth. Both backends must serve exactly its routes, and backend/HTTP-V1.md describes the behaviour behind them. Two extensions carry Bento-specific facts: x-bento-codes lists the problem types an operation can return, andx-bento-local-only marks routes that exist only outside production, such as the billing simulator.

Generated types

make api-generate writes TypeScript types from the spec into both web frontends, mobile and the k6 workloads; make api-check fails when any copy is stale. The generator has its own dependencies in contracts/http.

Breaking changes

CI compares the spec with the merge base on main using oasdiff and fails on a breaking change. Deliberate compatibility adjustments are recorded in breaking-changes-approved.txtwith their rationale; the current entries cover authentication and account-contract tightening.

Events

Every event shares envelope.v1.schema.json: id, type,source, occurredAt, provenance and data. The type ends in its version, as in account.membership_added.v2. A changed payload gets a new version file, and the old one stays listed as superseded until producers stop emitting it. The optional client IP in provenance is personal data, and only Audit reads it.

ProducerSchemasExamples
Identity41sign_in_succeeded, mfa_enabled, deleted
Account18created, membership_added, purged
Files12upload_completed, trashed, storage_changed
AI8run_requested, run_succeeded
Chat2message_sent, access_denied
Integrations17API-key, webhook, and MCP lifecycle facts
Privacy5export_requested, export_ready
Authorization3role.created, role_assignment.granted
Billing6subscription.changed, payment_failed
Audit1entry.recorded
Notifications1notifications.inbox_changed
Operations3Staff and maintenance lifecycle facts

Capabilities and assertions

Capabilities are listed with their callers on the architecture page. Across services, three signed assertions carry identity. All use Ed25519, and each has test vectors both backends must pass.

AssertionCarried inProves
PrincipalX-Bento-PrincipalWho the edge authenticated. A JWT with a lifetime of at most 120 seconds; the edge issues 60.
Client contextX-Bento-ClientThe client's IP address, for Audit, as seen by the edge.
ServiceAuthorization: BearerWhich service is calling /internal/capabilities/<name> or /internal/routes.

The edge learns which service owns each route from GET /internal/routes, refreshed every 30 seconds from the services in EDGE_UPSTREAMS. Edges return 404 for /internal/*and strip any assertion a client sends. How every failure is reported is on the Errors page.

Realtime

GET /v1/realtime upgrades to a WebSocket with the subprotocol bento.realtime.v1 and exchanges JSON text frames of at most 16 KiB. The connection is refused with 403 for a foreign origin, 401 without a session, and 429 beyond 20 connections per principal on one instance.

Client operations

subscribe, unsubscribe, presence.join, presence.update, presence.leave, signal, ping.

Server messages

welcome with a 25-second heartbeat, ack, error, hint, stream, presence, signal, pong, closing.

Topics

account:{id}, account:{id}:manage, principal:{id} and ai-run:{runId}, each authorized when subscribed.

Versioning

Additive changes stay in v1. Anything else becomes bento.realtime.v2.

Telemetry

Required

service.name (bento-<backend>-<role>), service.namespace,service.version, deployment.environment.name, bento.backend,bento.role and bento.modules. http.route is the registered pattern, and error.type is the failure code.

Never

IDs, emails, paths or messages as attributes; a principal, email, address or account on security metrics; any AI prompt, input or output; any realtime content; rate-limit or cache keys.

AI, audit and privacy

AI tasks and pricing

Versioned tasks cover document summaries, grounded fact extraction, image description, rubric-based evidence evaluation, draft-only responses and a workspace assistant. Task definitions include bounded inputs, structured outputs and usage pricing; Go and Nest catalog parity is checked in CI. Retrieval contracts cover lexical/semantic search, while inbound MCP exposes scoped AI tools through OAuth and explicit consent. See contracts/ai/README.md and contracts/auth/mcp-oauth.md.

Audit chain and export

hash-chain.md defines how each entry hashes over the previous one, with vectors.export.md defines the JSON Lines feed written to S3 every 5 minutes.

Personal data inventory

inventory.yaml lists every personal data field, payload, object, mail and log, with its purpose, lawful basis, retention and erasure path. See Compliance.