For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt, and this page is available as Markdown at /reference/docs/adding-a-context.md.

Add a context

  1. Decide its owning service, business language, aggregate boundary and command/ query outcomes. Create services/<service>/contexts/<owner> with domain, application and driving/driven adaptor modules. The domain has no technology imports. A co-located context has independent database authority.
  2. Register { "owner": "<owner>", "service": "<service>" } in contracts/contexts.json. A read-only context without owned storage declares "persistence": "none"; it needs no database or migration manifest. Site-local SQLite contexts declare "persistence": "sqlite" and own their private journals without platform PostgreSQL credentials or migration enrolment. PostgreSQL contexts retain the default mode and add a migration manifest and SQL under adaptors/persistence/migrations. Runtime credentials cannot migrate schemas. Provision its database with the shared baseline schema and a restricted role, then apply scripts/migrate.py; schema checks reject owner/checksum mismatch. Beto also applies its service-owned delivery migration before the context migrations. Storage-free registrations stay outside this enrolment. A Beto context without broker contracts, events or subscriptions declares "messaging": "none"; it receives no broker user or credential.
  3. Add context-specific typed Protobuf payloads, independent application DTOs, event metadata in contracts/events/catalogue.json, and the owning service's transport payload encoder/decoder. Root envelopes stay business-neutral. Declare Beto request contracts in contracts/beto/messaging/catalogue.json with the owner's queries file pharos/<owner>/v1/queries.proto, its commands file pharos/<owner>/v1/commands.proto (either may be null, not both) and their binding consumers (api and/or metallo). Declared owners must differ even with underscores removed, since generated Go names drop them: model2 and model_2 would both generate Model2Query and model2v1. The contract is split by role: requests.proto holds the <Owner>Request, <Owner>Query, <Owner>Command and <Owner>Reply wrappers and RequestError; queries.proto the query inputs; replies.proto what queries return; and commands.proto the commands. Each file declares package pharos.<owner>.v1 and go_package github.com/pharos-ai-factory/pharos-platform/services/beto/contracts/requests/generated/pharos/<owner>/v1;<owner>v1, with underscores dropped from the final <owner>v1. Generation checks both and that each file holds exactly its role's messages. The files must exist under contracts/<owner>/messaging/proto/ and in no other context's proto root, and an undeclared role file is refused. Number a new owner's envelope this way: the request's command = 10 and query = 11; the reply's outcome = 10, error = 11 and results from 12; and each wrapper's alternatives from 10. Existing Project and Party Governance envelopes retain their field numbers; their frozen wire fixtures qualify compatibility separately. A declared contract generates Beto and API bindings before registration and receives no broker authority. A registered owner with requests names them and their callers in contracts/<owner>/messaging/broker.json; each kind needs its declared file and no caller is implied. Governance authenticates RabbitMQ UserId against generated owner-specific policies. Only the API may carry actors or use private authentication/provider-binding protocols; allow_actor defaults to false. Production callers remain API-only until downstream enrolment is reviewed and qualified. Regenerate and review the scoped topology permissions. Host provisioning reads the generated runtime_bindings; caller pharos_<name> uses <NAME>_BROKER_URL. Add each binding to the projected Secret of Argo's broker provision Job; beto_broker_deployment.py checks the list. Prove allowed delivery and refused authority. In Beto, generate command/query values, responses and owner mappings from annotated Protobuf. Declare private events in the service's event catalogue and consumers in services/beto/contracts/subscriptions.json. The generator produces typed event codecs, subscription names, paired command queues and replay membership. See the Beto adaptor map.
  4. Add a typed browser projection, its registered encoder and scoped gateway publisher credential only when the UI needs live state. Persist exact bytes and revision atomically; register server-side channel authorisation and owner reconciliation queries before mounting the feature. In Metallo, explicitly register None for an internal aggregate whose browser feature is deferred; persistence then saves its state and receipts without realtime rows. Unknown projection registrations remain errors. Beto and Themelio handlers explicitly publish through RealtimeBusPort; their requested snapshot or rejection must match committed state or recorded outcome.
  5. Compose its ports, driving adaptors and workers in the service's services folder. Keep DI registrations here and inject plain ports into application handlers. Go persistence uses support.Open("<owner>") and explicit consumer binding; Python uses its owning service's context/subscription registry. Themelio uses Dependency Injector under services/composition; extend its empty contract generator before registering the first context. Its service-local delivery migrations are applied by the administrator. See the Themelio scaffold guide. No foreign implementation import or synchronous second-root command is permitted. Beto commands use services/commandregistration and a handler factory with a per-invocation owner write repository. The shared transaction owns receipts, version checks and outgoing intent. Integration publication and durable command reactions run through their declared subscribers. List each Beto owner whose request worker the binary composes, with its request kinds, in services/beto/services/beto/di/owners.json. Broker generation and provisioning refuse a registered owner with requests that the file does not list, and a listed owner that is not registered; a DI test checks the file against the composed workers.
  6. When a browser feature needs access, explicitly bind its public route in the API, keeping its backend key separate from browser credentials. A Beto owner's HTTP translation is enrolled in services/api/adaptors/http/mappings/&lt;owner&gt;.json; it needs the owner's catalogue entry for api and creates no route. The enrolment check also runs Party Governance's existing specialised generator. Add workload secret references, restricted broker topic permissions, the administrator-only topology bootstrap and exact NetworkPolicy edges. No database or broker admin key enters an application.
  7. Regenerate contracts/checksum metadata, add owner-native behaviour tests and real transaction/delivery/recovery tests, then run all applicable lanes from TESTING.md. Add browser multi-window/history-gap tests for live projections.

The skeleton deliberately makes registration explicit. A new folder or catalogue entry alone grants no routes, queue, credential, publisher, subscription or business authority.

API command bindings

Declare each POST route and its JSON schema in the owner's HTTP contract. Register a CommandBinding using the API's independently generated owner request/reply types. It reads {id}, Idempotency-Key, If-Match, optional X-Correlation-ID and the validated JSON body. The API supplies the authenticated session subject (or the separate provisioned CLI actor) as actor_id; headers/body cannot choose it. The typed request uses a fresh transport ID on every attempt while retaining the command ID. Owner errors and committed outcomes map to HTTP status codes. RouteCommandBinding also serves DELETE routes without a body, builders that read other route values, and creates named by their Idempotency-Key (KeyTarget). Queries that check member access carry the same subject in QueryMetadata.

Beto validates actor metadata and exposes it as CommandContext.ActorID to the handler. Authorisation remains owner policy. Present actors contribute to the idempotency fingerprint; changing actor produces a conflict. Empty actors are reserved for trusted internal/legacy commands and convey no user authority. Project's published request surface remains query-only; technical fixtures exercise this public command path.

Page source

Source snapshot · Revision f928834c8.