Mirrors Cleanverse query_apass into IdentityRegistry.upsertIdentity. The
registry is the on-chain source of truth; nothing in Covenant's lifecycle
(confirmObligation, originate, fund, settle) ever calls Cleanverse
directly — this is the only piece that does.
cd relayer
npm install
cp .env.example .envFill in .env:
IDENTITY_REGISTRY_ADDRESS— from aDeploy.s.sol/Demo.s.solbroadcast.RELAYER_PRIVATE_KEY— must matchIdentityRegistry.relayer()(the address passed to its constructor, or set later viasetRelayer).CLEANVERSE_MODE=mockfor offline deterministic fixtures (src/mockFixtures.ts), orliveto hit the real sandbox.
npm run syncReads subjects.json (override with SUBJECTS_FILE), queries each address,
writes upsertIdentity on-chain, and prints what it wrote. One-shot by
design — not a watch/poll daemon (trivial to wrap in a loop later if needed).
- mock — fixtures in
src/mockFixtures.ts, keyed by lowercased address. The shipped fixtures reproduce exactly the tier/jurisdiction valuestest/Base.t.solandscript/Demo.s.solseed viaadminSetIdentityfor anvil's default mnemonic accounts 1-4, so a mock sync is directly diffable against the existing test suite (verified 2026-07-26: identicalisVerified/tier/jurisdiction, and a realconfirmObligation+originateagainst relayer-written identities repriced 97%/88% by tier, matchingHappyPath.t.sol). - live — POSTs
query_apassto Cleanverse. WithoutCLEANVERSE_API_IDit uses the public no-auth skills surface (CLEANVERSE_SKILLS_URL, defaulthttps://uatapi.cleanverse.com/api/skills) — confirmed live and reachable 2026-07-26, samemonadchain config (chainId 10143, aUSDC address) as AgentCheckout's earlier sandbox session. With anapi-id/api-key(Gateway Member, issued over the hackathon Telegram) it upgrades to the authenticated Cooperate endpoint (CLEANVERSE_BASE_URL) — same interface either way.CLEANVERSE_API_KEYis accepted but currently unused (no encrypted write endpoint is called;query_apassis a plain read).
subjects.json ships with anvil's default dev accounts for local testing —
replace it with real supplier/obligor/financier wallets before a live Monad
run.
This relayer used to end with an open question: real A-Pass tier is whatever
Cleanverse returns numerically, and an early ReceivableNote configured rates
only for {1,2,3}, so an obligor whose tier landed outside that set reverted on
originate rather than mispricing. The note said not to guess ahead of real
data.
Real data settled it. Every A-Pass issued to the demo cast came back with
tier = 50 — Cleanverse assigns tier itself, identically, so it cannot
carry an application's risk banding. The axis an integrator actually sets at
issuance is subTier (0–99), which is what generate_apass takes and what
this relayer sends from apass-roster.json.
So Covenant prices on subTier, through descending bands rather than a mapping
on discrete values — see src/CovenantDefaults.sol for the curve and
ReceivableNote.advanceRateBpsFor. No bucketing function was added here: the
relayer still passes Cleanverse's numbers through untouched.