> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.

# Party Governance

Party Governance in Beto owns canonical Party authentication and verified provider
identity associations. The <a href="/source/services/beto/docs/party-project/plan.md.txt">implementation plan</a>
and <a href="/source/services/beto/docs/party-project/identity-model.md.txt">identity model</a> 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 <a href="/source/services/beto/contexts/party_governance/adaptors/messaging/authentication/README.md.txt">authentication boundary</a>
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
<a href="/source/contracts/party_governance/messaging/proto/pharos/party_governance/v1/events.proto.txt">Party contracts</a>;
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.

<details class="page-source"><summary>Page source</summary>

<a href="/source/services/beto/contexts/party_governance/README.md.txt">Source snapshot</a> · Revision `f928834c8`.

</details>
