# 0046 — Service communication and composition Status: Accepted. Date: 13 October 2026. All services follow the same communication and dependency injection boundaries. Business commands, queries and integration events between services use versioned, typed Protobuf messages over RabbitMQ. Driving adaptors translate wire contracts into context-owned application types. This keeps transport and framework concerns outside domain and application code. Browser-facing HTTP, external integration protocols and private health and diagnostics endpoints remain at their designated boundaries. Browser updates use the server-authorised Centrifugo protocol defined in [CLIENT-SUBSCRIPTIONS.md](../../CLIENT-SUBSCRIPTIONS.md). Dependency registrations and lifecycle hooks belong in service composition roots. Application handlers receive explicit, provider-independent ports. Go composition uses Fx; other languages implement the same boundaries with idiomatic tooling. Worker cancellation and draining precede resource disposal, including after partial startup failure. Broker topology is provisioned through administration tooling. Runtime roles have scoped permissions. Publications use durable messages, mandatory routing and publisher confirms. Queries use correlated request/reply messages; the consumer acknowledges a query delivery only after its reply is confirmed and routable. Uncertain reads can be repeated. Command and event consumers acknowledge after the owner transaction commits state, receipts, recorded outcomes and outgoing intents. Retries preserve message identity and use bounded retry, dead-letter and explicit replay policies. Contexts without owned storage declare `persistence: none`. PostgreSQL remains the default for persisted contexts. Administration and migration generation omit storage-free registrations. This extends decision 0034, “fresh service-first skeleton”. These rules apply across services. Context documentation and published contracts specify individual operations, payloads and implementation status. Architecture checks and transport integration tests verify the shared boundaries and delivery guarantees as each service adopts them. ## Beto adoption — 14 October 2026 Beto groups shared implementations under `adaptors/{persistence,messaging,realtime,http}`. Context adaptors group repositories under `persistence` and wire mappings under `messaging`. Foundation packages contain inward-facing primitives and technical configuration, diagnostics and cancellation helpers. Architecture fixtures reject context imports in shared adaptors, boundary drivers in foundation, database drivers in delivery, and network delivery drivers in persistence. Generated Beto outputs have explicit paths. Its selective generator includes only the service's envelopes and owned request contracts; generation checks compare missing, changed and obsolete outputs without changing source. Pinned Staticcheck and errcheck join formatting, vet and architecture checks in `pnpm check:beto` and `pnpm verify`. The Project query manifest generates exact broker resource and topic permissions. Runtime has no configure permission, queue write permission or exchange read permission. A separate topology user receives the registered resources and exact binding keys with no shared-topic publication permission. Dead-letter declaration requires read on the query queue and write on its delivery exchange; those capabilities belong to the administration process. The host retains management provisioning authority and preserves runtime credentials. Each administration pass issues a fresh bootstrap credential; it is excluded from workload secret mounts. Reconnecting managed clients applies changed broker authorisation. This adopts the applicable improvements through reference commit `ed737e1`. Project's read-only model has no aggregate or domain events. The initial adoption deferred command infrastructure; the foundation adoption below supersedes that deferral. Wire contracts, migration bytes and acknowledgement order are preserved. See the [Beto adaptor map](../../services/beto/adaptors/README.md). ## Beto query registration — 15 October 2026 Comparison through reference `3c8b080` adds scoped query/reply identifiers generated from compiled Protobuf payload oneofs. The composition root registers each named application handler with its owner input and reply mappings. A provider-independent `QueryBusPort` carries plain query/result values; the shared messaging adaptor owns dispatch and wire validation. Registration fails before startup for invalid or foreign identifiers, missing callbacks, duplicate handlers and contract mismatches. Architecture fixtures enforce composition ownership and generated identifiers. Broker consumers also require envelope visibility to match the registered subscription. Existing private-owner checks and acknowledgement order remain. No message identity, schema, queue, migration or Project result changes. The initial query adoption deferred durable command reactions and explicit realtime buses; the foundation adoption below supplies those capabilities. ## Beto schema-owned query contracts — 15 October 2026 Reference `a9d7bf6` adds schema-generated plain inputs, responses, checked encoders and response-bound registrations. Beto adopts these for Project. A Protobuf message option declares each public query's owner and success response; generation validates the declaration against its owner query/reply envelopes before writing output. Unsupported shapes and invalid ownership/visibility fail generation. Plain models live in `contracts/requests/generated/model/` and contain no Protobuf runtime dependencies. Application handlers may import their own context's plain models; published read-model names may alias them. Domain imports remain independent of contracts. Owner encoders and typed registrations live in separate packages under the context's `adaptors/messaging/generated`. Composition uses the generated registration to bind the exact handler result and checked encoder. Handlers select success or an application request error. Encoders preserve optional presence and reject ambiguous responses and integers outside the cross-language safe range. Shared dispatch redacts encoding failures and supplies the validated request identity when constructing the final reply. Complete-list behaviour, nullable legacy fields, queue identities and acknowledgement order are retained. Pre-change request/reply byte fixtures verify wire compatibility. Negative architecture fixtures reject wire imports into application, contract imports into domain, duplicated public shapes and handwritten query registration. ## Beto command and delivery foundation — 16 October 2026 Complete the applicable Storefront infrastructure adoption through `a9d7bf6`. Plain command handlers receive generated values, a narrow aggregate target, a per-invocation owner write repository and an explicit realtime bus. Composition binds handlers directly to the shared transaction coordinator. Remove the earlier callback store, command port, generic Fact and HTTP command/query helpers. The coordinator commits aggregate state, version, receipts, outcome, private facts, explicit realtime intent and public command reply bytes atomically. Private reactions commit command bytes before a paired consumer executes them. Integration publication and realtime subscribers own separate receipt/outbox transactions. Source revisions, times, IDs and exact saved bytes survive retry. Subscriber completeness, private codec pairing, retries and replay are checked against generated declarations before workers start. Shared plain request values come from Protobuf. Required presence applies before dispatch and while encoding replies. Event declarations generate plain values, codecs and names. Application architecture checks use Go type information to reject transaction callbacks, handler chaining and aggregate mutations outside command handlers. The historical compatibility gate is independent of generation drift. The API generates Project's mechanical translation from its explicit ACL. Keep Pharos UUID derivation, queues, query wire fields, HTTP output and frozen SQL baselines. Add Beto's checksummed delivery migration separately, through the administrator only. Project stays read-only and storage-free; its command, event and realtime registries remain empty. Project-specific provisioning is retained. Technical fixtures verify commands and recovery using isolated native PostgreSQL; no demonstration context enters production composition. ### Durable publication back-off — 24 October 2026 Beto, Metallo and Themelio use the same deployment-independent retry policy for persisted event, private-command, contact-projection, reply and realtime outboxes. The first failure waits one second. Each subsequent lease generation doubles the window up to five minutes, with equal jitter between half the window and its cap and a one-second floor. The existing persisted generation counts abandoned claims as attempts; `available_at` persists the sampled deadline under the current lease fence. Restarting a relay preserves both. Successful completion retains the record; a new publication starts its own retry sequence. No schema or wire change is needed. Unroutable mandatory publications remain pending until the intended consumer route is provisioned. They have no automatic discard threshold. A confirmed broker return leaves the channel usable, so the relay schedules that record and continues with other due work. Missing consumers must be resolved through their owning composition; an unowned sink queue would conceal the missing delivery contract. Publisher confirms, mandatory routing, immutable bytes, receipts and access controls remain required. Replies retain their existing expiry, which also caps their next deadline. Publication-worker connection and database failures use a separate one-to-thirty second jittered back-off, reset after a successful operation. Opening a socket alone does not reset it. Cancellation interrupts waits. These defaults apply to production and development alike; normal first delivery remains immediate. The five-minute cap reduces sustained outage traffic while permitting automatic recovery. Business workflow retries, bounded consumer dead-letter transfer and API request deadlines retain their separate policies. Real PostgreSQL checks cover persisted deadlines, restart recovery, stale leases, unchanged material, fresh-record progress and reply expiry. Broker qualification checks mandatory returns followed by successful delivery on the same connection. This controls retry amplification; storage latency still requires independent capacity and durability monitoring. ## API reply isolation — 16 October 2026 API connections own temporary exclusive classic reply queues. Each connection supplies an owner-scoped UUIDv4 reply address in AMQP `ReplyTo`; Beto validates it before dispatch. Query replies use that address with mandatory publication and confirms. Command transactions save the address with immutable reply bytes, and recovered dispatches preserve both. Reusing a request identity with another address is a conflict; retrying a command through a new connection uses a fresh request ID and the same command identity. Existing stored replies and requests without `ReplyTo` retain the legacy owner route. This introduces one narrow exception to administrator-provisioned topology: API runtime credentials can declare and bind their temporary reply queue family. Owner workers retain no configure permission. Topic permissions constrain both API bindings and owner publications; wildcard and foreign-owner addresses fail closed. Administrators still provision all exchanges and durable queues. Queue lifetime follows the API connection; disconnection releases waiting calls and retries use a new connection address. See the [Project transport contract](../../contracts/project/messaging/README.md) for the compatibility route, rollout order and real broker qualification. ## API commands and authenticated actors — 16 October 2026 Bindings receive the authorised HTTP request and resolved operation schema. `CommandBinding` maps POST path identity, idempotency/version/correlation headers and validated JSON into an owner-typed Protobuf request. The broker client supports both command and query routing. API tests compile the existing technical fixture into an independent API module; production Project remains query-only. The additive `CommandMetadata.actor_id` field carries the authenticated subject. The server supplies a distinct actor for CLI credentials. Beto passes the subject through plain metadata to `CommandContext.ActorID`; business handlers can apply owner policy without HTTP dependencies. A present actor contributes to receipt fingerprints, preventing replay under a different user. Absent actors retain legacy/internal compatibility and grant no authenticated-user authority. New HTTP commands require an actor. A pre-upgrade receipt retried with a new actor conflicts; its uncertain outcome must be resolved before submitting a new command identity. ## Themelio Python adoption — 16 October 2026 The Python scaffold follows the reference Operations foundation, including connection-specific reply destinations and authenticated command actors. Its plain metadata and handler context carry the actor; receipt fingerprints include present actors. Administrator-only, checksummed service migrations retain reply routes alongside immutable bytes. Empty production registries grant no context or broker authority. Test-only request contracts exercise the complete transport and transaction path. See the [Themelio guide](../../services/themelio/README.md). Contexts may share one physical PostgreSQL instance while retaining separate logical databases and credentials. Shared deployment provisioning and live qualification are covered by PR #697. API generation and broker provisioning remain Project-specific and must be generalised when adding the next context. ## Beto request catalogue — 21 October 2026 `contracts/beto/messaging/catalogue.json` declares each Beto request contract with its Protobuf file and consumers; only the API consumes them for now. Beto request bindings, API consumer bindings and API HTTP mappings are generated for every declared owner in its own namespace. Drift checks report missing, changed and obsolete outputs. A contract may be declared before its context is registered; it then receives no broker user, queue or credential. This replaces the Project-specific generation and provisioning recorded above. Broker authority stays explicit. An owner that offers requests needs a catalogue entry and must name its callers in `broker.json`. A missing caller list fails instead of opening every offered kind to the API. Until Beto authenticates request callers it accepts only the API's identity and reply route, so the API is the only permitted caller. A Beto registration may declare `"messaging": "none"`, like `persistence`; it then cannot declare broker contracts, events or subscriptions. An owner without requests, event publications or subscriptions receives no broker user. The generated `runtime_bindings` name each principal's credential, with `pharos_` using `_BROKER_URL`. Host provisioning, the Argo deployment check and integration bindings read them. The administration image packages every owner's contracts and Beto's contexts. Project's wire contracts, queues and effective permissions are unchanged; its generated permission patterns differ only in grouping. Test owners live under `scripts/fixtures/` and are activated in temporary trees, so no demonstration context enters production registries. See [adding a context](../adding-a-context.md). ## Governance reconciliation — 23 October 2026 The generic catalogue and process manifest now include Party Governance and the persistent Project context. Existing message identities and field numbers survive their move into role-separated files. API Go and Metallo Python bindings derive from the same declarations; binding generation grants no deployed consumer access. Governance authenticates broker requests using RabbitMQ-verified `UserId` and generated owner-specific caller policies. An owner must declare every caller. `allow_actor` defaults to false; only the API can enable it or send the existing private authentication/provider-binding protocols. A caller's identity and reply family remain consistent across owners, while its kinds and actor authority are scoped per owner. Production enrolment remains API-only. This supersedes the earlier pre-authentication restriction in the catalogue adoption above. Party and Project retain separate PostgreSQL databases, credentials, migration histories and durable delivery. Their private protocol workers are covered by the runtime-owner manifest and real broker composition checks. The source worker and unconnected downstream consumers remain paused. ## Beto owner checkpoints — 25 October 2026 Beto publishes recoverable Project and Party views for Execution Core, Pipeline Coordination and Workload Portfolio. Each consuming context has one authenticated principal across both owners and independent durable event, retry and dead-letter queues. Its query permissions are limited to the authority and synchronisation operations declared by each owner. The restricted all-Party contact inventory is reserved for an explicitly enrolled support caller. A consumer binds its queues before asking an owner to materialise a checkpoint. The owner uses a repeatable-read transaction to save immutable items, exact revisions, deterministic hashes and an ordered manifest. It commits a checkpoint marker to a dedicated outbox in the same transaction. Pages are stable while the checkpoint is valid; an expired checkpoint restarts from the beginning. Project inventory is scoped to the enrolled provider, instance and source scope. Party bootstrap selects canonical IDs discovered in Projects, while the support inventory covers Parties outside Projects. A checkpoint marker is advisory. RabbitMQ ordering applies within one queue and channel, with no ordering guarantee across queues, owner streams or reconnects. A consumer can claim convergence only after its exact IDs, independent revisions and hashes match the owner checkpoint and every relevant buffered event through those revisions has been applied. `ProjectChanged` and `ProjectAccessChanged` retain separate receipts even when they share a Project revision. Authentication, provider-binding and contact revisions are also independent. Duplicate or older facts cannot replace newer stream state; retirement and removal evidence remains part of comparison. The same subscription-before-snapshot procedure supports initial load, daily comparison, repair and recovery after local data loss. Counts are diagnostic only. Synchronisation proves agreement about the published shared fields. Current permission and continuing-Run authority remain separate owner decisions. This section supersedes the API-only enrolment state recorded on 23 October 2026.