Architecture
How the pieces fit
Six participants, one event stream and a pod per customer. The diagrams are rendered from Mermaid sources in apps/microsite/diagrams/.
System view
The thick path is the renter's journey. Dotted lines are asynchronous or trust relationships. The CDR leg sits entirely inside Ping CDR Kit stacks - CommBank's data-in stack on one side, a full data holder stack per bank on the other, and the kit's Register test harness anchoring trust.
Participant isolation
No two participants share a Docker network. Cross-participant calls go through published ports, exactly as they would between Railway services - so the local and hosted topologies match.
Tokens never leave the kit
The pod asks the kit PingAccess for data with its own client-credentials token and an X-CDR-Subject header. The kit rule finds the arrangement, refreshes the bank token if due, and swaps it in.
Events from Ping, not app code
PingDataSync is the kit's own way of reacting to token-store changes. A Server SDK sync destination publishes to Redis, with durable, resumable delivery from the changelog.
Trust model
This is the kit's trust profile, unchanged. The Register signs CommBank's software statement assertion and issues every participant's certificate from its CA.
| Register SSA | PS256, iss=cdr-register, signed with key _GLOBAL_. Its jwks_uri points at the kit PF's /pf/JWKS - CommBank's software product key. |
|---|---|
| DCR | POST {issuer}/register, application/jwt signed with the software product key and carrying the SSA, over mTLS with a Register-CA client certificate. The kit issues client_id = dcr-sso-<nameUUIDFromBytes(software_id)> and allows one registration per software id. |
| mTLS | PingAccess at each data holder requires the client certificate. Tokens are certificate-bound (cnf.x5t#S256), so every token, API and revocation call must present the same certificate. |
| private_key_jwt | Client assertions with header typ: client-authentication+jwt (PF 13 rejects JWT), aud = the DH issuer. |
| PAR | /as/par.oauth2 with a PS256 request object carrying claims.sharing_duration and claims.id_token.acr, PKCE S256. response_type=code id_token, the id_token encrypted RSA-OAEP / A256GCM. |
| Revocation | ADR → DH: POST {issuer}/data-holder/arrangements/revoke with a client assertion over mTLS. DH → ADR: the kit servlet at /arrangements/revoke checks a DH-signed bearer JWT against the DH JWKS from the Register. |
Consent - from the offers 302 to consent-complete
CommBank's web layer only renders the CX screens and tells the kit who the customer is, through a Reference ID drop-off. Everything after that is kit config.
Async pod creation
Nothing calls the pod directly after consent. The kit's token store changes, PingDataSync notices, and an event on cdr.arrangements does the rest. The consumer acks only once the arrangement is in the pod's manifest; failures retry with back-off and poison messages go to a dead-letter stream.
Issuance
MATTR has no issuer_state on auth-code offers, so the binding that holds is the CBA PingFederate sub flowing into the claim source. The claim source merges identity with the pod's income output, with the purpose credential-issuance checked against the customer's use consents.
Proof verification
The proof says one thing - income is at least the threshold - and the pod's signature ties it to the same commitment that sits in the credential. The verifier checks the Groth16 proof on the server and again in the browser.
Deletion lifecycle
A pod can't outlive the consent that created it. The sweep runs every 15 seconds; once-off consents keep income for a short window (10 minutes in the demo) after it's ready, so the customer can claim the credential and make proofs, then the data goes.
CDR Data Pod internals
Every program runs through runProgram, which checks eligibility first: the arrangement is active, the consent type allows it, and every required scope and use consent is present. A refusal is audited and comes back as 403.
| Program | Needs scopes | Needs use consent | What it does |
|---|---|---|---|
| token-manager | - | - | Client-credentials token from the kit PF for the kit PingAccess. Lives on one call stack, never stored or logged. |
| loader | accounts.basic, transactions | - | Accounts → balances → 365 days of transactions, one step event per stage. |
| refresher | accounts.basic, transactions | - | Ongoing consents only. Unattended refresh within a daily call budget, then income again. |
| income-aggregator | transactions | income-verification | Groups credits by payer, finds the cadence, drops outliers, scores confidence, annualises net pay. |
| zkp-prover | transactions | income-proof | Poseidon commitment, Groth16 proof of income ≥ threshold, ES256 JWS over the commitment. |
| retention / destroyer | - | - | Deletes compartments on revoke, expiry or purpose fulfilled; destroys the pod and shreds its key. |