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/services/beto/contexts/project/README.md.

Project context

The proposed Party Governance and Project evolution plan defines the next persistent model, phased Waldur handover, owner contracts and draft OpenAPI replacements for Themelio routes. The source-managed canonical slice and retained directory have separate contracts.

Canonical source-managed Project

The Project aggregate owns canonical identity, metadata and complete child memberships. A source reservation allocates its UUID independently of the exact provider/instance/external reference. The enrolled source scope and management generation remain fixed for this iteration. Only complete qualified source reads change its active state; source failures, inventory absence, not-found and access-denied responses cannot close a Project. The provider is the source of truth for the Project's existence: when a direct read of the enrolled Project reports it removed (Waldur is_removed), the refresh worker closes it with the operator close command. An open Project missing from a complete inventory is read directly, so its removal is observed.

Source roles become project-administrator or developer through the Project adaptor. Unsupported roles remain observations and grant no capability. Member references resolve through Party Governance's published binding port; unresolved references remain in the complete observation. An absent qualified administrator is a governance issue. The slice does not invent an administrator or implement native management writes.

ObserveExternalProject runs as a plain application command inside one owner transaction. Material changes raise ProjectChanged and, where effective access changes, ProjectAccessChanged with affected Parties, including removals. Successful unchanged reads advance separate source-status evidence while preserving the material Project revision and emitting no new domain facts. Recorded receipt replay also preserves the original acquisition time.

The canonical service queries are ResolveProjectProviderBinding, GetProjectIntegrationSnapshot, ListProjectIntegrationSnapshots and CheckProjectCapability. Point snapshots are complete and bounded to 256 source members; overflow requires recovery and never silently truncates access. The inventory materialises immutable, scoped pages of canonical snapshots at one checkpoint. A capability decision requires fresh complete source evidence, a currently established exact Party binding at its retained revision, and current Party authentication eligibility. The default source freshness budget is two minutes, with a five-minute configuration ceiling. Runtime qualification determines operational availability and revocation bounds.

The published capability matrix separates Project information, Run state, Run outputs, launch, manual retry, cancellation, Workload and Pipeline operations, and access administration. The three broader project.* capabilities remain transition aliases for #794 only.

Project authority has its own revision stream. An initial reservation or a real source notice within the retry budget publishes pending invalidation; routine scans preserve the last complete evidence until it expires. A first or changed complete-read failure publishes failed authority. Repeated identical failures back off, then probe every five minutes after eight attempts. A successful complete read restores ready authority automatically; operator recovery can bring the next probe forward. Each successful complete read publishes one ready renewal with its original observation time and absolute validity. An unchanged read advances this authority revision without advancing the Project aggregate. The transition and exact public event bytes commit together. Replay retains the old authority timestamps. Confirmed deliveries are cleaned after 24 hours. The authority queues retain messages for 305 seconds; consumer restart and repair use the published current snapshot. See the authority delivery contract.

Consumers merge this stream with material, Party and binding views and fail closed while rebuilding or after expiry. The owner query remains the decision-time gate until consumers qualify equivalent local checks; separate event queues do not form an ordering barrier. The contract guide records the measurable bounds, transition callers and accepted-work continuation policy. The operator composition services/project-operations accepts close, security-cancel and advance-management. Closure is permanent. Security cancellation and management handover advance the management generation, fencing previously accepted work while leaving current membership intact. The aggregate, command receipt, material events, authority invalidation and source-lease fence commit together. A source refresh preserves closure and uses the new generation; it cannot reopen the Project or restore an older generation. An operator decision preserves the last complete-read timestamp and queues a fresh acquisition within the existing retry budget. Exhausted sources retain their periodic recovery probes. Project-scoped member suspension also needs Beto-owned commands and integration facts. The accepted-work policy requires immediate suspension of personal Project access and Site account delivery (#765) to retain the exact native account and binding needed to finish accepted work or authorised cancellation and cleanup. This owner workflow remains pending implementation.

See the application workflow for owner ports, recovery, query evidence and source attribution. Metallo owns its local projections and consumer receipts. Native Project writes, fulfilment and browser subscriptions remain separate work. Source Control also owns its GitLab group/member effects and durable projections. Its owner-query adaptor and scopes remain disabled pending qualification.

Runtime and enrolment

Beto opens Project's isolated PostgreSQL database alongside Party Governance. Canonical broker queries authenticate the publisher and require explicit PROJECT_QUERY_SCOPES: a JSON array of subject, provider, instance and scope. It defaults to an empty array. Execution Core, Pipeline Coordination and Workload Portfolio have separate broker principals, queues and exact Project query scopes. The Waldur registration supplies those scopes to Beto's source Secret; provider credentials remain confined to Beto. For Waldur, scope uses the customer's lower-case UUID without hyphens, matching the source adaptor's normalised enrolment and complete-read evidence.

The source worker is disabled by default. PROJECT_WALDUR_ENABLED=true requires the complete installation/customer scope, read-only API token, STOMP credentials and verified CA bindings. Configuration lives in the composition root. Runtime defaults are a 30-second inventory interval, 30-second fenced database lease and 25-second acquisition deadline. Permission checks fail closed when source evidence is pending, failed or older than the configured freshness budget. With the accepted five-second source clock skew, the default absolute revocation ceiling is 125 seconds after a change immediately following a complete read; the maximum configured ceiling is 305 seconds. Delivery and consumer progress share the remaining validity window and are observable from the event's transition time and the consumer's committed apply time. Live qualification must establish normal availability and lag below those ceilings.

In the dev-box deployment, registration targets namespace pharos, pod label app: beto and Secret beto-project-source. It supplies restricted reader and STOMP credentials, and writes the enrolled scope alone to Secret metallo-project-source, which Metallo's views load for (#780). Project consumes STOMP 1.2 over verified TLS on port 61614. Only committed notice/quarantine receipts permit ACK. REST acquisition and periodic inventory use separate Project ports. No Project/Party provider credentials are installed in Metallo. Production execution workers remain paused while their consumer implementation is completed separately.

Project resolves bindings and checks eligibility through Party Governance's published query contracts. The composition root supplies the in-process, scope-authorised query handlers; Project's adaptor imports only those published contracts. Cross-context writes, implementation imports and foreign SQL are absent. New bindings are picked up on a subsequent complete source acquisition.

The retained login, AUP and provider writers stay authoritative. The Waldur 8.1.3 infrastructure adaptation requires live login/AUP and registration qualification before deployment. See the migration provenance and gates.

Retained static directory

GetProjects is a read-only query returning the complete static directory. There is no aggregate, write command, database, migration, event or browser projection associated with this directory operation.

The application query receives ports.ProjectReadRepository. Its Go read model preserves the legacy Themelio GET /api/models/projects fields: numeric id, name, path, full_path, nullable description and avatar_url, visibility, web_url, created_at and updated_at. The numeric ID remains an opaque legacy provider binding. It grants no canonical Pharos identity.

The mock returns two deterministic example projects and allocates fresh data for each read. All trusted service callers receive the same list. It performs no network access. The legacy endpoint filtered GitLab subgroups using the caller's token and excluded groups marked for deletion. A future real adaptor needs an explicit user-scope design, complete pagination, deletion filtering and provider error mapping before replacing the mock.

The web application uses ListMyProjects through GET /api/v1/me/projects. Project derives membership from the authenticated Party actor and checks current project.view authority for every result. It returns paginated canonical UUIDs, revisions, names, organisations and lifecycle. The retained directory remains available for historical consumers and fixtures. See the member listing contract.

The composition root registers GetProjects through its generated response-bound registration. The schema generates the plain input/result models and owner codecs. The service's shared request registry handles dispatch, envelope validation, complete handler registration and reply encoding. Provider failures become redacted temporarily_unavailable replies; invalid envelopes go to quarantine. Beto's composition module binds the mock and registry through Fx and runs the RabbitMQ consumer; DI and wire dependencies remain outside application packages.

The registry dispatches plain application values through QueryBusPort. The application handler selects the complete Projects response; its published read model aliases the generated plain Project. The checked encoder preserves nullable fields and rejects integers outside the JavaScript-safe range. Encoding failures produce redacted temporarily_unavailable replies. Generated identifiers preserve the existing wire names. Registration rejects missing mappings, invalid or foreign identifiers, duplicate handlers and input/reply type mismatches before workers start.

See the wire contract and decision 0046.

Verification

The query tests cover legacy fields, empty results, independent mock data, malformed and foreign contracts, and redacted provider failures. Registry tests cover duplicate and mismatched registrations, independent query dispatch and cancellation. Composition tests cover graph resolution, partial-start cleanup and worker draining. The RabbitMQ lane checks reply correlation, quarantine, restricted permissions and redelivery after an unroutable reply, then repeats the query against the built service image.

Recorded on 13 October 2026 for the slice based on fb798afca:

LaneResultEvidence and scope
Beto query and DIPassedGo race tests, vet and builds; legacy field shape, independent mock values, typed wire validation, redacted failures, resolved Fx graph, partial-start cleanup, worker draining and operational-only HTTP.
Real integrationPassedpnpm test:integration: PostgreSQL and RabbitMQ regression lanes, 135 Python infrastructure tests, all six images and Chromium session/realtime regression. Beto's typed query round trip also runs against its built image.
Project brokerPassedRestricted owner and caller roles, idempotent topology bootstrap, repeatable reads, malformed-request quarantine, denied topology/foreign reply access and redelivery after an unroutable reply.
Full host verificationPlatform limitationpnpm verify stops at three existing Metallo launcher tests on macOS: Bash 3.2 empty-array handling and /var versus /private/var path resolution. All four launcher tests pass unchanged in the pinned Linux Playwright container.
Remaining verification stepsPassed separatelyAll three Go modules' race tests/vet/build, frozen Python/Ruff/Mypy and 954 unit tests, Web type-check/build, Kubernetes renders, credential checks and all Ansible syntax lanes.
Registration and development setupPassedArchitecture/security negative fixtures, generated contracts, large-file review, storage-free context registration, and focused dev-box credential/source/cluster tests. Adding the Project broker binding preserves existing credentials.
Live environments and UATNot runMinikube/k3s rollout, actual NetworkPolicy enforcement, dev-box takeover, operator UAT, remote CI, real Themelio/provider integration and production deployment remain separate checks.

Reference improvements — 14 October 2026

Beto adopts the reference's service/context adaptor organisation, selective contract generation, explicit generated output locations and pinned Go static checks. See the Beto adaptor map for navigation and commands. The legacy project fields, static results and wire identities remain unchanged. At that stage Project had no persistence authority or browser subscription.

The broker manifest declares its query boundary. It generates exact resource and topic permissions for Project and a separate topology role. Beto runtime users cannot configure resources, bind queues or bypass routing through the default exchange. API callers have the scoped temporary-reply-queue capability in the transport contract. The topology role can redeclare registered resources and bind the registered query/reply keys. RabbitMQ requires queue read and delivery-exchange write for dead-letter declarations; that limited capability remains confined to administration.

Host provisioning preserves the Project runtime password and returns a fresh TOPOLOGY_BROKER_URL for the current administration pass. It is never mounted in Beto's workload. The standalone topology executable requires the pharos_beto_topology identity. Kubernetes provisioning restarts Beto after applying permissions to replace cached authorisation; retired queues retain their evidence until explicit administrative cleanup.

Verification for this update on 14 October 2026:

LaneResult
Beto static and native testsPassed: formatting, generated drift, architecture/large-file review, vet, pinned Staticcheck/errcheck and race tests.
Contract generation and toolingPassed: complete regeneration, selective Beto generation/checks, 65 tooling tests and 8 development-source tests.
Real infrastructurePassed: pnpm test:integration, including PostgreSQL, RabbitMQ permission refusals, repeatable scoped topology declaration, reply recovery, 135 Python infrastructure tests, all images, built-image GetProjects and Chromium.
Full host verificationpnpm verify reaches the same three existing macOS launcher failures recorded above. All four launcher tests pass unchanged in the pinned Linux Playwright container with its required socket mount.
Remaining platform checksPassed separately: all Go modules' race tests/vet/build, frozen Python/Ruff/Mypy and 954 tests, Web types/build, both Kubernetes renders, workload credential/configuration checks and Ansible syntax lanes.
Live environments and UATNot run: Minikube/k3s rollout, live NetworkPolicy enforcement, dev-box takeover, operator UAT, real provider integration, remote CI and production deployment.

Reference alignment — 15 October 2026

Reviewed the reference through 3c8b080. Project now uses schema-derived query/reply identifiers and direct composition with owner mappings and QueryBusPort. Shared broker consumers reject envelope visibility that disagrees with their subscription. The complete static listing and published Protobuf bytes retain their contract. The reference's durable command reactions and explicit realtime publications remain future work for a write-capable context. See decision 0046.

LaneResult
Full verificationPassed: pnpm verify, including shared API/Beto static checks, 70 tooling tests, 207 development-tool tests, all Go race/vet/build checks, 954 Python tests, Web types/build, Kubernetes renders and Ansible syntax. The earlier macOS launcher failures are resolved.
Real infrastructurePassed: pnpm test:integration, including PostgreSQL, RabbitMQ, 135 Python infrastructure tests, all six service images, the built-image Project query and Chromium session/realtime checks.
Generation and boundariesPassed: full contract regeneration, selective Beto drift checks, generated-name and composition negative fixtures, owner/type/callback validation and subscription visibility tests. Container-based gofmt also preserves standard input.
Live environments and UATNot run: Minikube/k3s rollout, live NetworkPolicy enforcement, dev-box takeover, operator UAT, real provider integration, remote CI and production deployment.

Generated query contracts — 15 October 2026

Reviewed the subsequent reference commits 33d464a and a9d7bf6. The Project schema now owns the plain input/result types, response association, checked codecs and typed registration. The existing application read-model name aliases its generated resource type. See the contract guide for generation, supported field shapes and numeric limits.

pnpm verify and pnpm test:integration both passed after this update. This includes all Go static/race/build checks, architecture and generation fixtures, Python and Web checks, real PostgreSQL/RabbitMQ, all service images, the packaged Project query and Chromium sessions/realtime. Schema-evolution tests compile and exercise generated Go encoders with optional, nested and repeated fields. Historical fixtures preserve exact request/reply bytes; legacy JSON keys and null values also pass. Generation prunes retired models/adaptors and preserves handwritten owner files.

Both lanes passed again after integrating API endpoint commit 2796ab049. The API generates the schema metadata within its own module; concurrent broker queries and the built API's GET /api/v1/projects round trip through Beto also passed.

Live UAT, Minikube/k3s rollout, NetworkPolicy enforcement, dev-box takeover, real provider integration, remote CI and production deployment remain unrun.

Main integration — 16 October 2026

Merged main through 78d20394c. The Argo administration image now provisions Project's broker credentials and topology before API and Beto start. Project remains storage-free alongside Metallo Site Runtime's private SQLite journals. API retains the native listener settings introduced on main.

pnpm verify, full contract regeneration and architecture checks passed on the merged tree. This includes API/Beto static checks and native race tests, the executable listener checks, 104 tooling tests, 344 development-tool tests and 1,221 Metallo tests, Web types/build, Kubernetes/Argo renders and Ansible syntax. The development-tool suite skipped five launcher tests because the expected Docker socket was absent, and one Linux process-identity test on macOS. All 11 focused broker provisioning tests passed after adding coverage for mounted credentials and secret-free output from the administration entry point.

The merged tree has not rerun the real PostgreSQL/RabbitMQ, built-image, Chromium or edge lanes while container testing is paused. Native dev-box takeover is pending release of another checkout's workstation connection. Live k3s rollout, NetworkPolicy enforcement, UAT, real provider integration and production deployment remain unverified for this merge.

Project operator commands

Run services/project-operations from a controlled maintenance environment with PROJECT_OPERATIONS_DATABASE_URL set to the restricted Project owner database credential. This executable is the independent management authority; its commands have no member HTTP or RabbitMQ registration and its credential is absent from Runtime containers. The canonical operator ID is audit attribution. Enrol the same provider, instance, scope, initial generation and source freshness as Beto.

The request is an immutable JSON file, reused verbatim after an uncertain result:

{
  "ID": "10000000-0000-4000-8000-000000000001",
  "ProjectID": "10000000-0000-4000-8000-000000000002",
  "OperatorID": "10000000-0000-4000-8000-000000000003",
  "ExpectedVersion": 7,
  "Action": "security-cancel",
  "Reason": "Compromised execution",
  "At": "2026-10-29T10:00:00Z"
}

Invoke the executable with -request command.json -provider waldur -instance INSTANCE -scope SCOPE -initial-generation 1 -source-freshness 2m. A stale revision refuses without changing Project state. Replaying a successful command returns its receipt without advancing the generation or renewing authority. Reusing its ID with changed material is an idempotency conflict. Existing outbox publishers deliver the facts; Execution Core and Pipeline Coordination save policy cancellation on observing a permanent fence, then recover native stop and cleanup independently of membership.

For an exhausted source, use the same executable with -recover-source. The request contains ID, ProjectID, OperatorID, ExpectedGeneration, Reason and At; omit Action and ExpectedVersion. Read generation, failure_attempts, last_failure, last_attempt_at and exhausted_at from the scoped pharos.project_sources row before preparing it. Resolve the provider problem first. The response is resumed, not_found, not_exhausted or conflict, with the recorded source generation. Resume resets the eight-failure budget and makes work due ahead of the next periodic probe, retaining the failure and memberships. A later notice can mark authority pending; a complete read must succeed to restore ready authority. Recovery works before a Project aggregate exists. Reuse the exact request file after an uncertain result; an old receipt cannot resume a later exhaustion. GetProjectMembers serves the API’s Project member view after a current project.view decision. It returns the complete bounded membership as canonical Party IDs and roles, the Project revision and a count of unresolved bindings. It validates exact Party binding evidence and rechecks caller authority and the Project/source revisions before returning. It exposes no provider IDs or guessed profile names. repositories.delete grants Project administrators the authority checked by Source Control’s durable deletion workflow; developers remain denied.

Page source

Source snapshot · Revision f928834c8.