# Party Governance Party Governance in Beto owns canonical Party authentication and verified provider identity associations. The [implementation plan](../../docs/party-project/plan.md) and [identity model](../../docs/party-project/identity-model.md) describe its wider scope. Its runtime owns the isolated `pharos_party_governance` database, authenticated broker queries, private authentication ingress, attribute delivery and canonical integration publication. The [authentication boundary](adaptors/messaging/authentication/README.md) describes enrolment. The retained login edge remains optional until its configured AAI flow is qualified; an unenrolled edge cannot register a Party. ## Member profiles `PartyProfile` is an independent aggregate keyed by the existing canonical `PartyId`. It owns the member's editable display name, biography, location, website, social handles and `private`/`public` visibility. Visibility defaults to private. Its revision is independent of authentication, AAI attribute projections and `authVersion`; editing a profile cannot change those values. `GetMyProfile` checks current Party authority and returns an explicit empty, private profile at revision zero when no profile has been saved. The query writes nothing. `GetPublicProfile` is an API service query for a canonical Party ID; missing and private profiles both return `not_found`. It exposes only saved user-authored profile fields. No AAI data, email or authentication state is copied into this public view. `UpdateMyProfile` replaces the complete set of profile fields. The trusted actor must own the target Party, and current eligibility and authentication generation are checked before receipt replay. The transaction holds an authentication row lock through commit so concurrent disablement is ordered with the update. Expected revisions, command identity and material remain stable on retry. Unchanged values retain the current revision and produce no publication, including an empty initial update at revision zero. Each changed profile commits its snapshot, receipt, outcome and typed principal-targeted realtime projection together in the Party Governance database. The target is the owner's canonical Party ID, including when visibility is public. There is no provider workflow or pending business state, and profile updates publish no authentication or verified-contact integration event. ## Domain authority `PartyAuthentication` is one aggregate keyed by a canonical `PartyId`. Registration receives that ID and an exact, verified issuer/subject pair from the application boundary. The aggregate preserves both strings, including case and whitespace. It has no email or username matching rule. Issuer enrolment and authentication evidence verification belong to trusted application ports and their adaptors. The owner repository must enforce uniqueness of the exact issuer/subject pair across Parties under concurrent registration. API authentication v3 also requires an exact opaque `voperson_id`, unique across all Party roots. An existing exact issuer/subject can adopt its first unclaimed person identity atomically. A changed person identity is rejected. The same person identity arriving under another issuer/subject returns `recovery_required` and preserves the canonical Party. Registration establishes enabled platform access, an enabled external identity and `authVersion` 1. `SetAccessState` accepts `enabled` or `disabled` with an `administrative` or `security` reason. Each actual change advances `authVersion`; an unchanged state produces no event or new generation. Disabled access prevents authentication eligibility. Approved authentication recovery replaces issuer/subject while preserving the person identity, provider bindings and disabled access state. It advances `authVersion` once and permanently reserves retired authentication identities. The owner capability requires independent operator approval; runtime composition currently exposes no administrator recovery route. `PartyProviderBinding` is an independent aggregate keyed by a canonical `bindingId`. It associates a Party with an opaque provider/instance/external-ID tuple and retains a private proof reference. Its states are `established` and `retired`; retirement is terminal. Repeating retirement leaves its original reason intact. Allowed retirement reasons are `administrative`, `security`, `identity_recovery` and `provider_account_removed`. The binding's Party, external identity and proof remain immutable. The owner repository must enforce uniqueness of the external identity across current and retired records; a retired association cannot be reassigned by creating another binding. The application checks current Party authority and verifies the proof before recording a binding. Provisioning, account readiness, Project membership and provider roles have separate owners and lifecycles. ## State and facts Both roots expose value snapshots for owner persistence mappings. Root revisions belong to the transaction boundary; `authVersion` describes authentication generation. Restoring invalid state returns a corruption error, which remains distinct from an expected command rejection. Restoration raises no events. The domain raises `PartyRegistered`, `PartyAuthenticationChanged`, `PartyProviderBindingEstablished` and `PartyProviderBindingRetired`. These are plain context-local values. Public mapping uses the [Party contracts](../../../../contracts/party_governance/messaging/proto/pharos/party_governance/v1/events.proto); the owner transaction supplies the root identity and revision and commits outgoing intent with its snapshot, receipt and outcome. Domain events carry no issuer, subject, proof reference or profile attributes. `Events` returns a copy for inspection. The owner repository validates and captures those facts before the aggregate leaves its transaction. `DrainEvents` can clear the pending domain facts; the persistence adaptor does not need it because each loaded root is scoped to one command. Snapshots and event values contain no mutable references to aggregate state. Each command changes one root. Neither aggregate has an in-progress workflow in this slice. Accepted authentication schedules a durable attribute observation. The owner worker claims a fenced lease and commits projection progress and its completion receipt together. Failed or interrupted work remains retryable. Qualified `site_username` observations schedule a separate binding command for each enrolled Site instance. Every target retains its own command and binding identity and its independent outcome. This work is optional and does not delay login or AUP. It rechecks current authority and source order, and requires explicit retirement before replacing an established Site identity. Provider fulfilment remains a later workflow. Existing Themelio login and AUP handling continue through the retained implementation. ## Runtime boundary The API may register a qualified login and query its authenticated Party. `ResolveAuthenticatedParty` also lets the API service principal resolve an already registered exact issuer/subject/`voperson_id` binding for delegated login. It returns current eligibility and generation without registration, adoption, recovery or mutation. Party actors and other service principals cannot use this identity lookup. Execution Core, Pipeline Coordination and Workload Portfolio use separate broker principals for the exact Party queries and events required by their consumer-owned views. Source Control has a dedicated broker principal. Its production owner-query adaptor and exact provider read scope remain disabled pending qualification. `GetCurrentParty` requires the API's trusted Party ID and generation to agree with the requested actor and current owner authority. Binding reads require an explicit provider/instance scope enrolled through `PARTY_PROVIDER_READ_SCOPES`; its default is empty. Qualified provider-binding writes use a private API relay with separately enrolled signed ownership facts. The owner rechecks exact Party identity and generation, and retired bindings remain fenced on receipt replay. Provider-binding reads keep their separate enrolled scope. Administrative mutations retain application preflight and persistence adaptors, with no public mutation route. Enrolling an issuer in `PARTY_API_AUTHENTICATION_V3_ENROLMENTS` activates stable-ID cutover: historical v1/v2 can resolve exact legacy roots before adoption, but cannot register new Parties or authenticate adopted roots. Without enrolment the legacy behaviour remains unchanged. API mode migration must enrol its issuer. Existing sessions retain their ordinary generation and expiry checks. The self-query preserves qualified given/family names, preferred username, home organisation, person identity, Site username, verified email and optional affiliation and entitlement lists. Missing lists and explicitly empty lists remain distinct. Affiliations and entitlements are source data; they confer no platform or Project role. `PartyIdentityAttributesChanged` publishes only display name, verified email, given/family names and their projection revision/generation. A dedicated projection outbox commits that public contact intent with the source-derived projection. `PARTY_SITE_BINDING_ENROLMENTS` qualifies exact issuer-to-Site-instance mappings; its issuer must also be enrolled for v3. One issuer can name several Site instances; repeating the exact issuer/instance pair is invalid. The configured claim must be appropriate for each named Site. The default is empty. Site work has `pending`, `completed` and `rejected` states, with a retained result code. The binding describes association only; Site contexts own native account access. When a Site is added, the worker backfills missing targets from each Party's current successfully applied observation. It uses the same generation and source-order checks and publishes ordinary binding facts; no consumer database repair is needed. An absent current claim cannot revive an earlier claim. Migration 0006 preserves the target, command identity and outcome of existing work. Private root facts enter the owner outbox in the root transaction. Separate subscribers retain their source identity, revision and occurrence time when committing public facts and publication receipts. `GetPartyIntegrationSnapshots` materialises authentication and binding state for selected Party IDs at one immutable checkpoint. `ListPartyContactSnapshots` gives the restricted support boundary a paginated inventory of all Parties, including verified contact fields, contact revision and access state. No deployed Metallo principal may call that support inventory. `GetPartyNotificationContact` is a separate exact-Party query for Execution Core's actorless caller. It exposes a verified email only when authentication is eligible and the current identity-attribute generation is available. Stale attributes return pending; disabled authentication or absent verified email returns unavailable. Authentication is rechecked after the attribute read. API, browser actors and other owner callers cannot use this query. Execution Core retains the current Project-authority check before delivery. The notification query and reply use wire field 19. Field 18 retains the deployed bound asserted-name operation; neither payload may decode as the other. Each receiving context owns its projection and receipts and retains separate Project permission checks; a Party event grants no Project permission. Site binding evidence remains an association with an external identity. Site Runtime continues to own native account provisioning and access. Beto adds no business HTTP route or Waldur call for synchronisation. ## Verification From the repository root: ```sh PHAROS_GO_PROJECT=services/beto python3 scripts/go.py test -race -count=1 ./contexts/party_governance/domain ``` Domain and application tests cover identity, generation, retained bindings, authorisation and stale observations. Run `python3 scripts/beto_native_integration.py` for restricted PostgreSQL atomicity/concurrency tests, and `python3 scripts/project_broker_integration.py` for the real broker, trusted registration, canonical query and public-event boundary lane. The API session lane is `python3 scripts/api_native_sessions.py`. Browser login/AUP, real AAI/provider qualification and cluster deployment remain separate environment-dependent checks.