Skip to content

Introduce the Catena-X Membership Hub - #15

Merged
paullatzelsperger merged 13 commits into
mainfrom
feat/implement_cx-membership-hub
Aug 20, 2026
Merged

Introduce the Catena-X Membership Hub#15
paullatzelsperger merged 13 commits into
mainfrom
feat/implement_cx-membership-hub

Conversation

@paullatzelsperger

@paullatzelsperger paullatzelsperger commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

Splits the member journey across two apps and removes the registration agent from the VPA orchestration:

  • The Onboarding API registers credential holders directly with the IssuerService (incl. the holder-attestation properties) and completes at confirmation — no Tenant Manager involvement, no NATS issuance subscriber.
  • The new Membership Hub (app + chart) drives the full journey: POST /api/members submits 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.
  • E2e suite, onboard-participant.sh / e2e.sh, charts and docs follow the new flow.

🤖 Generated with Claude Code

paullatzelsperger and others added 12 commits August 20, 2026 14:23
# 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
@paullatzelsperger paullatzelsperger added the enhancement New feature or request label Aug 20, 2026
@paullatzelsperger
paullatzelsperger merged commit 3995440 into main Aug 20, 2026
2 checks passed
@paullatzelsperger
paullatzelsperger deleted the feat/implement_cx-membership-hub branch August 20, 2026 13:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants