5executions on the deployed CRE Confidential Workflow
6Hedera testnet transactions, all SUCCESS
The thesis
Entitled to 4. Paid 0.
Investor B was sanctioned after he invested. He still holds 400 units, a dividend
entitlement of 4 and a validly signed, ACTIVE credential. The deployed
Chainlink Confidential Workflow read today’s evidence inside its TEE and decided he is not eligible today:
HOLD 4 — no transaction, no signature request. Investor A was paid 6 HBAR
on Hedera testnet.
Wallets are accounts.Credentials are claims.Authority is scoped.Identity persists.
The stack
Four technologies. Each with one job.
Catenor
Identity and scoped authority
What it is
The identity and authority layer: canonical identity (did:catenor), W3C credentials, scoped capabilities and delegation, policy decisions and a hash-chained audit.
Why we use it
Every actor gets its own identity; a wallet is only an account bound to it.
Every action needs explicit, scoped authority; delegation can never exceed the delegator.
Every decision lands in a hash-chained audit that anyone can re-verify (79 events, valid).
Chainlink CRE
Reads private evidence in a TEE, and decides
What it is
A deployed Confidential Workflow. Its secrets come from the Vault DON, and its Sumsub sandbox calls run inside the TEE.
Why we use it
It decides admission, credential issuance, offering eligibility and PAY/HOLD on private, current evidence.
Only facts and commitments leave the enclave; Catenor keeps no raw KYC response.
5 executions on the deployed workflow, all SUCCESS.
Privy
Which key can sign what
What it is
Server wallets whose Privy policies constrain which key can sign what. Owner keys stay outside the runtime; the runtime signer cannot change a policy.
SPV wallet: the ATS Factory and its own equity only.
Distribution Agent wallet: only Lisa or Bart, ≤ 20 HBAR.
Investor wallets: receive-only.
Hedera ATS
Runs the asset, verifiable by anyone
What it is
Asset Tokenization Studio on testnet: the tokenized SPV equity lifecycle — deployEquity, issueByPartition, the corporate-action role, dividend entitlements — and the payout.
Why we use it
ATS computes entitlements by ownership: Lisa 6, Bart 4.
Execution is public: 6 testnet transactions, each verifiable on HashScan and the Mirror Node.
01
Trust Anchor
Root of trust for trust-domain:catenor-one-demo. Admitted on the deployed CRE workflow.
did:catenor:656d…3cab
02
Sponsor
An organization with five scoped capabilities on spv:catenor-demo-001.
did:catenor:5b87…761d
03
SPV 001
“Catenor One Demo SPV 001”, a synthetic real-estate SPV. Its units are equity interests, not a deed.
did:catenor:bd78…5d44 0x182F…9926 · 0.0.10509879
04
Investor A
“Lisa Simpson” · fictional, Sumsub sandbox
Eligible when she invested, and still eligible at distribution time.
600 units · 60% 0x7de5…2473 · 0.0.10509881
05
Investor B
“Bart Simpson” · fictional, Sumsub sandbox
Eligible when he invested. Sanctioned afterwards. Still owns his units.
400 units · 40% 0xCfCa…Da73 · 0.0.10509882
06
Distribution Agent
Its own identity and wallet. Pays only after a delegated EXECUTE_DISTRIBUTION.
did:catenor:9b25…c074 0x9089…13E3 · 0.0.10511413
The demo
One continuous run. Sixteen decisions.
Instance c1-202609121659, 2026-09-12, 16:59 → 22:59 UTC. The maintainer ran every stage by hand,
one at a time, from an empty Privy app and an empty database to a paid distribution. Each chapter shows one
protocol decision, who made it, and which system executed it.
The Privy app starts with no wallets, no key quorums and no policies. Stage 10 creates the signer
infrastructure for the Trust Domain: a Bootstrap Endorsement Key (a Privy signature wallet,
message signing only, HKQY5…nVEk) under the Privy policyP_BOOTSTRAP,
the Privy policyP_ASSERT for credential-assertion keys, two runtime quorums and two management-owner keys that never leave the
maintainer’s machine. The acceptance rules are then hashed into the Bootstrap Configuration,
which becomes the input to the first CRE deploy.
The candidate organization gets a random did:catenor and an assertion key — a Privy signature wallet
(message signing only) — and proves it holds the key. Then the deployed Confidential Workflow opens the sealed context,
fetches its Vault DON secrets and makes two Sumsub sandbox calls from inside the enclave.
Six facts come out, all true, plus a commitment. Catenor keeps no raw Sumsub response. The admission policy
says ALLOW, the separate Privy signature wallet under the Privy policy P_BOOTSTRAP endorses, and the
Trust Anchor becomes ACTIVE: TRUST_ANCHOR_VALID: true.
The Sponsor is an organization with a random DID and no PII. At first it holds no capability. The Trust
Anchor signs a Relationship Credential (AUTHORIZED_SPONSOR_IN the Trust Domain),
which grants nothing by itself, and five Capability grants on
spv:catenor-demo-001, all ALLOW. The same capability on another resource is
DENYCAPABILITY_MISSING.
The SPV exists only after TOKENIZE_ASSET says ALLOW.
Catenor evaluated the Sponsor’s TOKENIZE_ASSET on spv:catenor-demo-001. Only then
were the SPV subject, its Privy EVM execution wallet and its Privy policy created. The Privy policy is
owned by a management-owner key outside the runtime, so the runtime signer cannot change it. It allows
eth_signTransaction only on chain 296 and only to the ATS v8 Factory. Everything else is denied.
Also createda private SPV_EXECUTION Account Binding, and SPV SPONSORED_BY Sponsor
Catenor capabilitydecides whetherthe SPV may exist·Privy policybounds whatits wallet may sign
Stage 30. SPV did:catenor:bd78…5d44 + wallet 0x182F…9926, created after the ALLOW.Privy policy of the SPV wallet.eth_signTransaction ALLOW only if chain_id eq 296 AND to eq the ATS v8 Factory.
The Sponsor signs offering:ab27ec9a-cd89-4b5b-92ea-bef19fbd9f9f: 1,000
equity-interest units in “Catenor One Demo SPV 001”. Eligibility is
policy:offering-eligibility:v1, pinned by hash. It requires four facts, and accepts one
credential type. The signature is checked against the Sponsor’s DEFINE_OFFERING_POLICY grant:
ALLOW.
“Lisa Simpson” and “Bart Simpson” are fictional Sumsub sandbox applicants, both forced
GREEN (Approved). Each gets a random did:catenor with no PII, a holder key — a Privy
signature wallet (message signing only) that signs presentations and never money — and a
receive-only Privy wallet: an owner key, no signer, no Privy policy. The link from DID to wallet is a private Account Binding,
never published in the DID Document. There is no CRE call here, by design: the evidence is read only inside
the TEE.
Investor A (Lisa)0x7de572532820B22040B561e68543419419b72473
Investor B (Bart)0xCfCa833A8e6b7651E44480b92822576BcB78Da73
Sumsub sandbox. Bart and Lisa both Approved (level id-only). Applicant IDs redacted.
Credentials are issued only after the TEE has looked.
Two INVESTOR_ELIGIBILITY runs on the deployed workflow read each investor’s current
Sumsub sandbox review behind the binding gate. Only after identity, AML and freshness came back true did the
Trust Anchor sign a W3C VC 2.0 CatenorInvestorEligibilityCredential (eddsa-jcs-2022),
valid from 2026-09-12 to 2026-12-11, with no PII. In stage 43 each investor signed a Verifiable Presentation
with the holder key; both pass 7/7 checks, and a replay with another challenge fails
HOLDER_PROOF_VALID.
Investor A run0xe66a410799c33f710fa19baefa09ea49f0d2f89aaced4266390d14cdee320b8f
Investor B run0x61485b72ad43bd2a63d466fdb7778d02d49a5dec4206979171aa68a15df064f8
Pacingthe runner waited 44 s between triggers: the deployed rate limit is 1 per 60 s
current Sumsub review→TEE facts + commitment→VC by the Trust Anchor→VP by the holder
CRE.3 successful, 0 unsuccessful. New: e66a41…0b8f and 61485b…64f8, about 63 s apart.
One sealed OFFERING_ELIGIBILITY run covers both investors. Inside
handlerInTee the workflow checked the holder proof, the VC signature, the pinned issuer key, the
subject, the validity window and the status, then the current Sumsub evidence and its
reconciliation with the credential, then policy:offering-eligibility:v1. Only minimized
conclusions left the TEE.
Investor
Units
Decision
Decision ref
Checks
A · Lisa
600
ALLOW
decision:8c0a84a1-d53f-4c2b-94f8-f88787ab53dd
7/7 · CONSISTENT
B · Bart
400
ALLOW
decision:3000f2f5-0376-446f-b9b9-bbffdd8078ef
7/7 · CONSISTENT
CRE execution0xfecb527d3d0133d4fd23a4c29a36b7998a1dc379c137a65175d72f4272464715 · 8 s
Run commitment0x83db064bab91f7dfd86dfd0309cbfe6f5d68f568116554fa2b21715b92599beb
Stage 44.A ALLOW 600 · B ALLOW 400, four requirements TRUE, 7/7 checks. Investor DIDs redacted.Logs tab. Every DON node emits only safe markers: secrets_fetched, offering_evaluated status=OK, handler_completed status=DELIVERED.
The SPV equity is deployed and issued — exactly 600 and 400.
A Privy testnet treasury, funded once from the faucet, activated the wallets. Then the Sponsor’s
TOKENIZE_ASSET grant was re-evaluated and the SPV’s Privy wallet sent deployEquity to
the ATS v8 Factory. Each issuance was bound to that investor’s TEE-backed ALLOW decision from stage 44 —
unused, within the offering total, and consumed on use. No generic mint.
The SPV wallet sends grantRole(ROLE_CORPORATE_ACTION, SPV) and then setDividend
(amount 1, decimals 2), each allowed only by its equity-pinned Privy policy rule. ATS computes entitlements by
ownership only. No funds move. Who is actually paid is decided later, by Catenor,
in the TEE.
grantRole0x726032ce…9f94 · 179,949 gas
setDividend0xaac4a085…f865 · 532,107 gas · record date 22:07:10 UTC · execution 22:10:10 UTC
10 HBAR→A = 6+B = 4(ownership only)
Stage 63.grantRole + setDividend confirmed; entitlements A 600 → 6, B 400 → 4.Read-only re-check, 22:08 UTC. 5 transactions SUCCESS; dividend 1: A 6, B 4.
The Agent has a wallet. It has no authority: DENY.
The Sponsor’s CREATE_AGENT is ALLOW. The Distribution Agent gets its own identity, its own Privy EVM
wallet bound privately (AGENT_EXECUTION), its own signer set, and a Privy policy narrower than the
SPV’s: chain 296, only to the two investor receiving wallets, ≤ 20 HBAR. Then
it asks to run a distribution before any delegation:
DENYCAPABILITY_MISSING.
The Sponsor signs a Relationship Credential, urn:uuid:87eb74d1-4314-49bc-bb4c-8ad6d3cb6794: Agent
AGENT_OF Sponsor. It is valid. The Agent asks again with only the relationship:
DENYCAPABILITY_MISSING.
Agent→AGENT_OF→Sponsor=no capability
Stage 71.AGENT_OFvalid; request with only the relationship → still DENY.
Before signing, Catenor checked three things: the action is explicitly delegable, and the Sponsor holds both
CREATE_DISTRIBUTION and DELEGATE_DISTRIBUTION_AUTHORITY on the same resource from
the ACTIVE Trust Anchor. The Sponsor then signed grant capability-grant:7bd93930-e21c-4f78-86fd-8f8c9aa46156
for 20 days, inside its own 30. The chain Trust Anchor → Sponsor → Agent evaluates
ALLOW. The Sponsor then tried to delegate TOKENIZE_ASSET: refused
ACTION_NOT_DELEGABLE, no signature requested. Investor A presenting the Agent’s grant:
DENYSUBJECT_MISMATCH.
Catenor did nothing here: the current external evidence changed. The operator set Investor B’s
Sumsub sandbox review to RED, reject label SANCTIONS, FINAL. He still holds
400 units on Hedera ATS and his entitlement of 4. His credential
urn:uuid:9c25804e-…4530 is still ACTIVE and validly signed.
400units still owned by Investor B
ACTIVEhis credential, still validly signed
Sumsub sandbox. Bart Rejected (Sanctions), Lisa Approved. Applicant IDs redacted.Stage 80. Review RED; credential still ACTIVE; holdings still 400. Investor DID redacted.
A 10 HBAR revenue event is recorded for the SPV (revenue-event:a2400be4…2355, an audit event; no
funds move). Investor A asking to run the distribution is DENYCAPABILITY_MISSING: only the Agent holds the delegated capability. Then one
sealed CONFIDENTIAL_DISTRIBUTION run on the deployed workflow, with three Sumsub calls from inside
the TEE, verifies each VP, VC and status statement, reads the current evidence, reconciles
it, applies policy:distribution-eligibility:v2 and computes 10 HBAR × units / 1000.
PAY 6Investor A · all TRUE · CONSISTENT
HOLD 4Investor B · VP 7/7 valid, but identity FALSE, AML FALSE · MISMATCH · FINAL, SANCTIONS
CRE execution0xfd2a96fb91bb4be494fd1f65a3e01ebd0e4da49d07520854cb38e2eb76e82d0b · 8 s
The approved plan goes through the Catenor signer boundary, then the Agent’s Privy policy, then a Hedera
transfer. The transfer goes to Lisa’s privately bound account, with empty calldata, on chain 296. For Bart,
no transaction is built and no Privy signature is ever requested.
+6 HBARInvestor A · tx 0x4781…ff24 · block 40440606
HOLD 4Investor B · transactionConstructed: false · investorBSignatureRequests: 0
TransferAgent 0.0.10511413 → Lisa 0.0.10509881: 6.00000000 ℏ (fee 0.024 ℏ)
Agent nonce0 → 1: exactly one transaction was sent
The distribution decision runs inside the deployed Confidential Workflow
identity-confidential-production, in handlerInTee. Sensitive evidence stays in the
enclave. What leaves is the executable conclusion and the commitments that let Catenor check it.
Confidential input
VPs · VCs · issuer-signed status · current Sumsub sandbox evidence · holdings
The sealed context opens only inside the TEE. The Sumsub credentials come from the Vault DON
(SUMSUB_APP_TOKEN, SUMSUB_SECRET_KEY; names only). Holdings were read from Hedera ATS
0xf37A…7ABC with balanceOf.
Computation
Verify → read current evidence → reconcile → policy:distribution-eligibility:v2 → 10 HBAR × units / 1000
The workflow owns the whole distribution decision — pro-rata shares and PAY/HOLD — not an
isolated eligibility check. Three http-actions SendRequest calls, 8 seconds, 0 credits.
A signed callback reaches the Railway relay. Catenor verifies the HMAC, the commitment, the
arithmetic and the holder set, then records one protocol Decision per holder.
Never leaves
No Sumsub response, name, document or applicant data.
Honest caveat: the coarse reason codes FINAL and SANCTIONS do reach
Catenor, and this page does not claim otherwise.
Investor A · Lisa · 600 units
PAY 6
ALLOW · every requirement TRUE · 7/7 presentation checks · reconciliation CONSISTENT · no reason codes
commitment 0x37df46f9…76db98
Then on Hedera: 6.00000000 ℏ from the Agent, tx 0x4781…ff24, SUCCESS.
Investor B · Bart · 400 units
HOLD 4
DENY · holds the asset, presentation valid 7/7 — but INVESTOR_IDENTITY_VERIFIED=FALSE,
INVESTOR_AML_CLEAR=FALSE · MISMATCH · FINAL, SANCTIONS
commitment 0x17b39d3e…4e8cf
Then on Hedera: nothing. No transaction built. Zero signature requests.
Execution0xfd2a…2d0bCRE UUIDa623ebaa-0fb5-48eb-ad70-c65f95f079eeWindow 22:40:08–22:40:16 UTCEvidence commitment0x824b…6405Plan pay 6.0 · hold 4.0
Judge evidence
Verify every system independently.
Public references, not just screenshots: HashScan links and a live Mirror Node check for every Hedera
transaction, CRE execution IDs from both the gateway and the platform, Privy wallet and policy IDs, and the
Catenor audit re-verification. Everything here is public-safe: no investor DID, no applicant ID, no VC or VP, no
key or secret.
24 of 24 stages. Every one ok: true.
Stage 99 (22:59 UTC, read-only) summarized every stage record and re-ran the Catenor verification. From
01-setup-env at 16:59 UTC to 83-execute-approved-distribution at 22:45 UTC.
24/24stages ran and are ok
5DEPLOYED CRE executions, all SUCCESS
6Hedera testnet transactions, all SUCCESS
79audit events, hash chain valid
3DISTRIBUTION_REQUEST_DENIED events
0signature requests for Investor B
Stage record
Modes: Local 2 · Sponsor live (non-spending) 12 ·
Confidential (CRE) · deployed 4 · Testnet live (spends HBAR) 6.
Stages 11 and 60 show their successful re-run; stage 50 ran twice and shows its second run (the Agent funding).
Stage 99. All 24 stages ran and are ok: true, each with its mode.End of demo.identity-confidential-production: 5 executions, all Success, $0.Stage 91.6 transactions SUCCESS; A 600 units / 7.0 ℏ, B 400 units / 1.0 ℏ.
The deployed Confidential Workflow.
Label: DEPLOYED. This is the deployed workflow on Chainlink’s private registry, not
cre workflow simulate. Five executions, all SUCCESS, about 8 s each, 0 credits used.
cre.handlerInTee(httpTrigger, onHttpTrigger, [{ tee: 'nitro', regions: ['us-west-2'] }]), routing five operations: admission, investor eligibility, credential, offering and distribution
Registry / DON family
private (Chainlink-hosted) / zone-a
Owner
0x7075057f1589BAf347cB6dD6a993B1FC536B8a0a
Deploy #1 · 17:38 UTC
ID 00e12517fc06c8984befaa63accbadb21b8c4abe529595fcf48542ba097ad250 binary 9902db587a3878b07ad03d599cb9c781f7c77b8ea440b168d8c42fd18b3fb9fb config 7cb69b7b82f6d8a0701ba39b15cc915d6ea3b4ca746c84fd3a805188c38098f8 — pins the Bootstrap Configuration hash; no issuer rules yet
Deploy #2 · 19:36 UTC
ID 0000e58d50da8eaf29fb4212c236f374d11d7eb2452502c3bad0afe74d3f25c8 same binary9902db58…, new config e7703b193460430b4767b89d44caea2620eda5b3c844deee69f9db9436207d1d adding credentialRules. The CLI updated the workflow in place
Pinned in deploy #2
credential type CatenorInvestorEligibilityCredential; the only accepted issuer did:catenor:656d66dff9ce1db692786ee241bc3cab#assertion-key-1, public key z6MknrBa8Vbrsz5YCoBhgmV54ALJP9p1YSocpeBvgzBgJVXq; status at most 600 s old; policy:offering-eligibility:v1 and policy:distribution-eligibility:v2
Vault DON secrets
CATENOR_INTERNAL_API_TOKEN · SUMSUB_APP_TOKEN · SUMSUB_SECRET_KEY (namespace main; names only, no value was printed)
Trigger
HTTP trigger with one authorized EVM key 0x18487BeFE194528cf429f5889BAE3aCbdA104a6c, called as JSON-RPC workflows.execute with an alg: ETH JWT
Gateway
https://01.gateway.zone-a.cre.chain.link
Result path
callback to the Railway relay https://catenor-one-production.up.railway.app/v1/internal/cre/identity-confidential/results; the local runner pulls each HMAC-authenticated result, re-authenticates it and delivers it
Uploaded configs
public data only: the callback URL, the hashes, the evidence-acceptance rules, the issuer key and the trigger address
The five executions
Execution IDs come from two places: the gateway’s workflow_execution_id and the platform’s cre execution list. From stage 42 on they ran on deploy #2.
Never: a Sumsub response, a name, a document or applicant data.
What this evidence does not claim
No independent attestation check. Catenor did not verify an enclave attestation itself; TEE placement is the CRE platform’s handlerInTee guarantee.
reportFromDon is not implemented. Offchain report verification is a P1 item.
Sandbox and mock evidence. The company KYB is a SYNTHETIC MOCK; the investors are synthetic Sumsub sandbox applicants. Nothing ran on mainnet.
Logs are not claimed to stay in the enclave. They are visible per node; only safe markers are logged.
Findings for Chainlink, reported as observed
The gateway URL. The docs’ private-registry gateway https://01.enterprise-gateway.zone-a.cre.chain.link/ answered -32600 Workflow not found for two ACTIVE private-registry workflows — the confidential one and a minimal non-TEE control workflow, catenor-http-control (0038de78…a66f). https://01.gateway.zone-a.cre.chain.link, the only gateway URL embedded in CRE CLI v1.33.0, accepted the same signed request at once (HTTP 200, ACCEPTED).
User logs are visible. The simulator banner says user logs “will not be visible, and will not leave the TEE”. On the deployed platform the Logs tab shows them from several DON nodes (Node 1, 2, 4, 5, 8, 9…). The workflow logs only non-sensitive markers through safe-log.
Several callbacks per execution. Each node’s copy calls back. The relay keeps the first authenticated result per run and answers 409 LATE_OR_DUPLICATE_RESULT to the rest.
Secrets.cre secrets create parses CRE_ETH_PRIVATE_KEY from the .env it is given, even with --secrets-auth browser; the template’s non-hex placeholder fails with invalid hex character 'Y'.
Enrollment. The first Confidential Workflow deploy on this account needed no extra enrollment step.
Screenshots
C3. 3 secrets created in the Vault DON (namespace=main). Only names are shown.C4 · deploy #1. Registered in the private registry (zone-a): 00e12517…d250, Active.E2 · deploy #2.Same binary9902db58…, new config e7703b19…; new ID 0000e58d…25c8.Before stage 11. Active, private, owner Catenor One, 0 executions.Stage 11.394f…4bd8: trigger + two SendRequest to the Sumsub sandbox.Stage 44 · Logs. Only non-sensitive markers, from every node.Stage 82.fd2a…2d0b: trigger + three SendRequest.End of demo.5 executions, all Success, $0 spend.
Catenor decides whether. Privy bounds what.
Development app catenor-one-ethonline-2026, created empty for this run. Every execution wallet has its
own management-owner key, kept on the maintainer’s machine and never in the runtime, and its own runtime signer: a
1-of-1 key quorum, override-scoped to the wallet’s Privy policy. The runtime can request signatures; it cannot change a Privy policy.
Wallets and Privy policies
Read back live in stage 92 and checked against the dashboard. Everything not listed is denied; exports are denied everywhere.
Wallet
Kind
Privy policy
What it allows
Bootstrap Endorsement Key HKQY5…nVEk
Privy signature wallet (message signing only)
Privy policyP_BOOTSTRAP vzww22mkxmtn4f333fdqpse7
signMessage only. Endorses the root Trust Anchor after an ALLOW; its own owner and runtime quorum
{ Investor A, Investor B }0x7de572532820B22040B561e68543419419b72473 0xCfCa833A8e6b7651E44480b92822576BcB78Da73
∧
value lte
20 HBAR0x1158e460913d00000 (18-decimal weibar)
DENYexportPrivateKey · DENYexportSeedPhrase · everything else denied by default: no ATS contract, no SPV wallet, no other chain.
The SPV’s Privy policy after the equity existed
The factory rule came first. After deployEquity, the SPV management-owner key (outside the runtime) added three Privy policy rules, each pinned to the new equity. Six rules in all.
chain_id = 296 ∧ to = ATS v8 Factory 0xd1F118A40f3b02883D35909eF2517e7EDd78379d
allow-issueByPartition-equity
ALLOW
chain 296 ∧ to = equity ∧ function issueByPartition ∧ partition = 0x…01 (default)
allow-grantRole-corporate-action-to-spv
ALLOW
chain 296 ∧ to = equity ∧ function grantRole ∧ _role = 0xa1acfc49…55cd ∧ _account = SPV 0x182F…9926
allow-setDividend-equity
ALLOW
chain 296 ∧ to = equity ∧ function setDividend
deny-exportPrivateKey
DENY
—
deny-exportSeedPhrase
DENY
—
Controls in action
Created only after authorization. The SPV wallet and its Privy policy exist only because TOKENIZE_ASSET was ALLOW; the Agent’s, under CREATE_AGENT.
Still not authorized. Before delegation, and with the relationship alone, the Agent’s requests were DENY CAPABILITY_MISSING.
Every signature fit one Privy policy rule. Each Hedera transaction was dry-signed first (signature discarded), then signed live under exactly one rule: five by the SPV, the payout by the Agent.
The HOLD holder got nothing. No transaction built for Investor B, no Privy signature requested: investorBSignatureRequests: 0, Agent nonce 0 → 1.
Separate signer sets
Bootstrap: its own owner key and runtime quorum …-bootstrap, signer for one wallet only.
Assertion keys: one owner and the …-assertion quorum for the signMessage-only Privy signature wallets.
SPV: a new, separate management-owner key and runtime quorum …-spv-runtime.
Agent: a new, separate owner key and runtime quorum …-agent-runtime. The treasury has its own pair.
Investors: one owner key per receiving wallet, no signer, no Privy policy.
Stage 92 read-back, as stored by Privy
Wallet
Owner
Additional signer
Privy policy (owner matches the wallet)
Rules
SPV 0x182F…9926
set
1, override-scoped
catenor-one-spv-execution-4c875d44
Factory (chain ∧ to); DENY both exports; issueByPartition (chain ∧ to ∧ function ∧ partition); grantRole (chain ∧ to ∧ function ∧ role ∧ account); setDividend (chain ∧ to ∧ function)
Agent 0x9089…13E3
set
1, override-scoped
catenor-one-distribution-agent-3d8dc074
ALLOW eth_signTransaction [chain ∧ to ∈ ∧ value ≤]; DENY both exports
Limitation. The Privy dashboard shows these wallets at $0.00 and lists none of their
transactions: Privy’s balance lookup does not cover Hedera (chain 296), and Privy only signed — the runner broadcast
the transactions. The money side is on HashScan (see the Hedera tab).
Screenshots
Stage 30. SPV Privy policy: eth_signTransaction ALLOW only if chain 296 AND to = ATS v8 Factory.Stage 60. SPV Privy policy: factory rule + the three equity-pinned rules; exports denied; default deny.Stage 70. The Agent’s Privy policy: chain 296 AND to in [A, B] AND value ≤ 20 HBAR.Stage 92. One Privy policy per execution wallet; override-scoped signers; Agent policy exactly the approved boundary: true.
Six transactions. All SUCCESS. Check them live.
Hedera testnet (chain 296), Asset Tokenization Studio v8 through the official Factory. Every transaction was
confirmed by the maintainer after a dry run that simulated the exact call and got a Privy dry signature. The buttons
below query the public Mirror Node from your browser.
Asset Tokenization Studio v8 contracts through the official Factory. Issuance:issueByPartition ×2. Configuration: the corporate-action role and the dividend. Lifecycle: the dividend entitlement plus the distribution.
Deployed and demonstrated on Hedera testnet, verifiable on HashScan.
An ineligible holder is visibly blocked: entitled to 4, received 0.
Screenshots
Stage 60.deployEquity: SUCCESS, block 40438071, fee 7.864 ℏ.Stage 63. Entitlements, dividend 1: A 6, B 4.Stage 83. Payout: 0.0.10511413 → 0.0.10509881, 6.00000000 ℏ.Stage 91. 6 transactions SUCCESS; A 7.0 ℏ, B 1.0 ℏ; entitlements A 6 / B 4.
Re-verified from signed documents.
Stage 93 (22:57 UTC, local, read-only) re-verifies signatures and chains from the recorded signed documents.
The operational database is an index, not proof.
Audit hash chain
valid
79 events in 28 types, append-only.
Trust Anchor
VALID
TRUST_ANCHOR_VALID: true, no failed checks. did:catenor:656d…3cab
Investor credentials
2 × ACTIVE
Both issuer-signed by the Trust Anchor. Bart’s is still valid, as the thesis requires.
Audit events by type
The counts include the three abandoned stage 11 attempts (ADMISSION_REQUESTED 4,
CONFIDENTIAL_VERIFICATION_REQUESTED 4, CONFIDENTIAL_EVIDENCE_VERIFIED 1).
INVESTMENT_EXECUTED is 2 of 4 INVESTMENT_AUTHORIZED: the dry runs authorize without executing.
The authority chain
Relationships, capabilities and delegation are public-key-verifiable authority with no sensitive input, so none of this runs in the CRE, by design.
Root Trust Anchor
Admitted · ACTIVE
did:catenor:656d66dff9ce1db692786ee241bc3cab
Authority from Admission on the deployed CRE workflow
scripts/demo/93-verify-catenor-audit.sh — re-verifies the audit hash chain, the Trust Anchor, the authority chains and the credentials from the recorded signed documents (the run records stay local and git-ignored).
scripts/demo/99-verify-complete-demo.sh — every stage record plus the Catenor verification, in one summary.
Screenshots
Stage 93. Audit hash chain valid (79 events); Trust Anchor VALID; Sponsor and Agent chains ALLOW; credentials ACTIVE.Stage 99. All 24 stages ok: true; the Catenor verification passes.
What went wrong, and how it was fixed.
None of these consumed a set-once state or duplicated an on-chain action. The full stage records stay local and
git-ignored; the public, reviewed record is artifacts/final-demo/RUN-LOG.md.
The day, in phases (UTC)
Time
Phase
Outcome
16:59
A · Local preparation
Instance c1-202609121659; Railway PostgreSQL with 0 Catenor rows, 5 migrations
Stage 11, attempts 1–3: the documented gateway does not serve this organization. Attempt 1 got
CRE gateway HTTP 400 with no detail; the verifier now surfaces the gateway’s code and message. Attempt 2
got -32600 Workflow not found, although cre workflow list showed the workflow ACTIVE. A
minimal non-confidential control workflow (catenor-http-control, 0038de78…a66f) isolated
the cause: the same signed request failed on the documented enterprise gateway and was
ACCEPTED on https://01.gateway.zone-a.cre.chain.link. The verifier’s default gateway changed
(overridable with DEMO_CRE_GATEWAY_URL). Attempt 3 ran while the fix was still being written. Attempt 4
was admitted. No attempt created an execution or consumed the set-once root.
secrets.sh failed on the CRE_ETH_PRIVATE_KEY placeholder. The CLI parses that
key from the .env it is given, even in browser mode: invalid hex character 'Y'. Fixed with
a fresh random, unfunded key, written without printing it. It is not linked to the account and is not the
trigger key.
Stage 60 recorded the asset reference instead of the address, after a successful deployEquity,
and failed with invalid equity or SPV address. The runner now records the checksummed address and the
reference separately, and never redeploys an existing equity. The 21:25 UTC re-run reported “already deployed …
no new deployment” and completed the owner-authorized SPV Privy policy extension. The equity was not deployed twice.
A transient CLI account error after deploy #2 (“unable to retrieve organization info… try again in a few
minutes”). A re-run a few minutes later was clean; deployed triggers do not depend on the CLI session.
An unneeded 25 HBAR SPV top-up in the second funding run: the script tops up any wallet below its
suggested amount. Accepted to save time; it is testnet HBAR and the SPV had no further transactions.
An earlier instance was stopped.c1-202609121438 was stopped after stage 11 because its
admission ran on CRE simulation. The final run restarted on a new Privy app so that the admission also ran on the
deployed workflow.
The gateway control experiment
Gateway
Result
https://01.enterprise-gateway.zone-a.cre.chain.link/ (documented for the private registry)
HTTP 400 -32600 Workflow not found, twice, including about 5 min after the deploy
https://01.gateway.zone-a.cre.chain.link (embedded in CRE CLI v1.33.0)
The Confidential Workflow identity-confidential-production on the Chainlink-hosted private registry, admission included. Not cre workflow simulate
Privy
Real development app
New app catenor-one-ethonline-2026: real server wallets, Privy policies and key quorums in a development environment
Sumsub
Sandbox
Synthetic applicants, level id-only; reviews set by the operator. “Lisa Simpson” and “Bart Simpson” are fictional
Company KYB
Synthetic mock
Used only for the root Trust Anchor admission
Hedera
Testnet
Chain 296. HBAR came from the portal faucet. Nothing ran on mainnet
Railway
API + relay
catenor-one-production.up.railway.app: API and CRE relay only, inert. Its Postgres holds the demo’s operational state
Still open after the run:reportFromDon / offchain report verification (P1), and deleting the
control workflow catenor-http-control.
Study notes
Explain it simply.
Short, honest answers for judges — and for anyone rehearsing the pitch.
Why does Catenor need a canonical identity?
Because wallets, providers and execution accounts change. The subject persists: a random did:catenor with no PII, and wallets attached to it through private Account Bindings. In this run every actor — the Trust Anchor, the Sponsor, the SPV, each investor, the Agent — has its own DID, and none of them is its wallet.
Why separate Relationship from Capability?
A relationship answers who someone acts for. A capability answers what they may do, on which resource, until when. The run shows it twice: the Agent’s AGENT_OF relationship was valid and its request was still DENY CAPABILITY_MISSING; only the delegated EXECUTE_DISTRIBUTION grant made it ALLOW.
What is actually being tokenized?
1,000 equity-interest units in “Catenor One Demo SPV 001”, a synthetic real-estate SPV, deployed as an ATS equity (C1SPV001) on Hedera testnet. The demo does not claim that a land deed is represented by the token.
Why does Bart still hold 400 units?
Because ownership and current payment eligibility are different things. Bart was eligible when he invested — the TEE said ALLOW 400 — and the units were issued. A later sanction does not silently erase ownership or his dividend entitlement of 4. It changes whether he may be paid today, and that is decided confidentially at distribution time: HOLD 4.
Why Privy if Catenor already handles authority?
Catenor gives the semantic decision: this Agent may distribute for this SPV, and this holder is PAY. Privy constrains what a specific key can physically sign, whatever the application asks, through its Privy policy: the Agent’s key can only send on chain 296, to Lisa’s or Bart’s receiving wallet, up to 20 HBAR. Two independent fences. Policy Decision ≠ Execution Authorization.
What exactly happens inside CRE?
In handlerInTee the deployed workflow opens a sealed context, fetches Sumsub credentials from the Vault DON, calls the Sumsub sandbox, verifies each VP, VC and issuer-signed status statement against the pinned Trust Anchor key, reconciles the current evidence with the credential, applies the pinned policy and — for the distribution — computes 10 HBAR × units / 1000 and PAY or HOLD per holder. Five operations, five DEPLOYED executions, about 8 s each.
What exactly leaves the TEE?
Decisions, requirement statuses, the seven check results, the reconciliation class, coarse reason codes (FINAL, SANCTIONS) and commitments. No Sumsub response, name, document or applicant data. Two honest caveats: the reason codes do reach Catenor, and on the deployed platform the workflow’s user logs are visible per node — which is why it logs only non-sensitive markers.
Why is the tokenization not in CRE?
Because it has no private input. Deploying the equity, issuing units and setting a dividend are public actions on Hedera, gated by public-key-verifiable authority (the Sponsor’s TOKENIZE_ASSET grant from the admitted Trust Anchor). The TEE is used where private, current evidence is involved: the admission, the credentials, offering eligibility and the distribution. The issuance amounts are still bound to TEE-backed decisions: 600 and 400 are exactly what the stage 44 run approved.
Why was the workflow deployed twice?
Deploy #1 carried the Bootstrap Configuration hash for the admission. The Trust Anchor’s key did not exist until that admission, so deploy #2 pinned it as the only accepted credential issuer. Same binary (9902db58…), new config, updated in place; the execution history survived.
Why does Bart get no signature request, rather than a refused one?
Because a HOLD holder never reaches the executor. The Catenor signer boundary builds a transfer only for a PAY holder, to that holder’s privately bound account, for exactly the plan amount. The Agent’s Privy policy is a second fence that was never needed for Bart: investorBSignatureRequests: 0, and the Agent’s nonce went from 0 to 1.
Is this production?
No, and the page says so everywhere. The CRE workflow is really deployed on Chainlink’s private registry, and the Privy wallets and Privy policies are real — in a development app. The investors are synthetic Sumsub sandbox applicants with fictional names, the company KYB is a synthetic mock, and the asset lives on Hedera testnet. Catenor did not independently verify an enclave attestation, and reportFromDon verification is not implemented yet.