Party Governance
Party Governance in Beto owns canonical Party authentication and verified provider
identity associations. The implementation plan
and identity model 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
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;
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:
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.
Page source
Source snapshot ยท Revision f928834c8.