Introduce the Catena-X Membership Hub - #15
Merged
Conversation
# Conflicts: # charts/cx-ve/Chart.lock
Replaces the Onboarding API's tenant-manager invocation: the onboarding process now creates the holder entry in the IssuerService itself (resource "sudo", scope issuer-admin-api:admin, mirroring the CFM registration agent) and completes right there — the CONFIRMED status callback fires within the registration call. EDC resource provisioning moves downstream of that confirmation (upcoming membership hub). - new HolderRegistrationService port + IssuerService adapter (409 = already registered, so re-drives stay idempotent) - state machine runs synchronously to COMPLETED; the NATS issuance subscriber, provisioning poll loop and holder-id correlation are gone (the lifecycle-event publisher stays); OnboardingState itself is unchanged - OnboardingProcess/entity drop the tenant-manager fields; OnboardingCompleted no longer carries a participantContextId - chart: jwtlet seed maps the SA to the "sudo" context with issuer-admin-api:admin only; issuer-service.* config replaces tenant-manager/identityhub; participant.dataplane/ccm move out - orchestration definition: registration-agent/registration-rollback removed from the VPA DAG (cfm.issuer VPA properties must keep travelling with the profile — the certo activity reads the BPN from them) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017nfecB3UB91xPzX2r1fGA8
New Spring Boot app driving a partner's full path into the dataspace across
the two halves the VE keeps apart: it submits the CX-0006 registration to the
Onboarding API (as OSP client "membership-hub" against the OSP IdP) and, on
the CONFIRMED status callback, provisions the EDC resources via the CFM
Tenant Manager — whose client code moves here from the Onboarding API. The
membership record correlates the hub-minted externalId (the id the callbacks
carry) with the participantContextId provisioning assigns.
- POST /api/members + GET /api/members/{externalId} (lazy Tenant-Manager
refresh on read); POST /api/callbacks/registration-status for the callback
- callback may arrive before the submitting call returns (the Onboarding API
completes synchronously); registering() never rolls that progress back
- profile deployment keeps sending the cfm.issuer VPA properties (the certo
activity reads the BPN from them); BPN is therefore required on ingress
- Postgres persistence (own membershiphub database, seeded by the chart) with
an in-memory "test" profile, mirroring the onboarding-api layout
- new charts/membership-hub (deployment/config, jwtlet seed at hook weight
230 mapping the SA to tenant-manager-api scopes, postgres seed, optional
HTTPRoute), wired into the umbrella; OSP IdP gets the membership-hub client
- install-ve.sh builds/loads the image; CI builds/tests and publishes it
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017nfecB3UB91xPzX2r1fGA8
The @bean method onboardingApiClient() collided with the OnboardingApiClient @service's default bean name, and bean-definition overriding is disabled, so the pod failed on startup. Renamed to onboardingApiRestClient. Added the missing context-load smoke test (mirroring the onboarding-api's), which catches this class of clash at build time. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017nfecB3UB91xPzX2r1fGA8
The seeded credential definitions attest via the holder attestation and
require bpn/memberOf/contractVersion/id from the holder's properties; a
holder created without them makes credential generation fail ("Failed to
apply mapping definition") and leaves the credential request stuck in
REQUESTED — the onboarding agent then retry-loops forever. Send the same
property map the CFM registration agent used to pass (id = DID, bpn,
memberOf = active agreement ids, contractVersion 1.0.0).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017nfecB3UB91xPzX2r1fGA8
Provisioning now runs inside POST /api/members: the registration submission
returns (the Onboarding API completes synchronously and its CONFIRMED
callback is recorded meanwhile), then the tenant + participant profile are
deployed and the returned profile id is stored on the record. The callback
no longer triggers anything — it only records CONFIRMED/REJECTED — and the
Tenant Manager poll loop is gone: GET /api/members/{externalId} resolves the
stored profile id and reads the profile's current state from the TM until
the membership is terminal. Provisioning still only happens on a CONFIRMED
registration (a rejected one is answered with a normal 200 too); a
registration without a synchronous confirmation surfaces as REGISTERING and
is never provisioned. New state CONFIRMED marks the callback's outcome.
The hub's HTTPRoute (/hub) is now enabled by default; install-ve.sh and
redeploy-ve.sh set its hostname from -H, and redeploy-ve.sh builds/loads the
hub image and restarts its deployment alongside the other apps.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017nfecB3UB91xPzX2r1fGA8
- root README: monorepo table and build/deploy sections cover the membership
hub; new "How a member gets onboarded" walkthrough of the split flow; fixed
the stale claim that install-ve.sh builds nothing (it builds and kind-loads
the three app images); documented redeploy-ve.sh and the /hub route with a
curl example
- scripts/README: install-ve.sh/vps descriptions updated, redeploy-ve.sh
documented (was missing entirely), onboard-participant.sh described as the
hub-driven full journey
- onboard-participant.sh now drives the Membership Hub: POST /hub/api/members
and poll GET /api/members/{externalId} until PROVISIONED (REJECTED/FAILED
exit non-zero, an unconfirmed REGISTERING exits immediately). OSP token
machinery, kubectl log-tailing and the --cluster/--namespace flags are gone
- e2e.sh onboards through the hub, gated on PROVISIONED — un-breaking its DID
check under the split flow
- install-ve-vps.sh: add the membership-hub host overrides (DID template +
route hostname) that a real-hostname install needs
- docs: issuance-flow attribution and the intra-VE component list updated;
sut-verification points at the hub for ve2's wallet/data-plane obligations
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017nfecB3UB91xPzX2r1fGA8
…ntProfileId/participantContextId Restores the original membership field names across the record, entity, port, docs and the onboard-participant.sh projection — the earlier rename also broke the participant-context extraction from cfm.vpa.state (the Tenant Manager writes "participantContextId"). Plus a debug log when an OSP registers its callback address. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017nfecB3UB91xPzX2r1fGA8
The suite's participants now come from the hub's full journey: new MembershipHubApi submits POST /api/members and polls the membership until PROVISIONED (failing fast on REJECTED/FAILED/REGISTERING), and the provisioned identities — participant context id included — are read straight off the correlated record, guarded against wire-contract skew. That removes the WireMock-callback wait, the DID-template mirroring and TenantManagerApi (which also exchanged under the onboarding-api jwtlet mapping that no longer carries tenant-manager scopes). The OSP-facing surface keeps its own contract test: register a status callback as osp-client, submit a registration directly, pin the CONFIRMED callback — the hub-driven tests no longer touch the shared osp-client callback slot. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017nfecB3UB91xPzX2r1fGA8
The rebase dropped it: upstream added the BpnCredential claimMapping to the transfer-type mapping in ParticipantOnboardingService, a file this branch deletes — its logic now lives in the hub's CfmTenantManager, so the mapping is restated there. Siglet stamps the caller's BPN into the flow token from its credential; the e2e assets carry no static bpn property anymore. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017nfecB3UB91xPzX2r1fGA8
The second half of upstream #13 lost in the rebase's modify/delete conflict: the cfm.dataplane VPA must carry the oauth2_token_exchange authorization profile so the control plane acquires a jwtlet token for its signaling calls — siglet 0.0.19 enforces signaling_auth, and without the profile every transfer died at flow prepare with 401 "Missing Authorization header". Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017nfecB3UB91xPzX2r1fGA8
wolf4ood
approved these changes
Aug 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Splits the member journey across two apps and removes the registration agent from the VPA orchestration:
POST /api/memberssubmits the CX-0006 registration and, once CONFIRMED, provisions the EDC resources via the CFM Tenant Manager;GET /api/members/{externalId}is the externalId ↔ participantContextId correlation record.onboard-participant.sh/e2e.sh, charts and docs follow the new flow.🤖 Generated with Claude Code