Repository navigation
v0.4.0 — configuration preflight, bring-your-own-host, and a stricter money path
CredentAgent is the consent layer for AI agents: before an agent completes something
consequential — a payment, an age check, releasing a record — a person has to prove a
credential from their phone wallet. This release adds a configuration check you can run in
one call, a supported way to plug the gate into your own application instead of the bundled
storefront, branding for the consent pages, and a safer ordering on the money path.
⚠️ Breaking change — only if you wrote your own external checker
An external checker is a plug-in that does the verifying and the charging on
CredentAgent's behalf, instead of using the built-in flows. If you wrote one against 0.3.1,
this release changes the contract you built to.
Your checker used to verify and charge in a single step. It now verifies in one step and
charges in a separate one, and CredentAgent decides when the charge happens.
The reason: CredentAgent's own rules can be stricter than your checker's. Your checker might
be satisfied by an 18-and-over proof where the shop requires 21-and-over. Under the old
arrangement the card was charged before CredentAgent got to say no — so a 20-year-old's
alcohol order would be charged and then refused. Charging is now something CredentAgent
authorizes, never the checker.
Three specific changes:
- Move your charging code out of
consume()and into the newsettle().consume()must
now verify only.settle()is optional — a checker that only proves identity, with no
payment involved, is never called and can leave it out. DelegatedVerdict.settlementwas removed. If you returned your receipt there, it is now
ignored. Return it fromsettle()instead. This one produces no error message — worth
searching your code for.binding.payeeis now required rather than optional. This one will fail to compile, which
is the intent.
Full migration guide with before/after code:
#161
If you use the built-in consent flows or the bundled storefront, none of this affects you.
New
credentagent.doctor() — check your setup in one call. Configuration mistakes used to
surface as a confusing failure halfway through a checkout. doctor() inspects your
deployment and hands back a plain list of findings, each with the problem and how to fix it.
Run it at startup or in a health check.
defineHost() — use the gate with your own application. Previously, wiring the gate into
something other than the bundled storefront meant assembling several pieces by hand and
knowing which internal slots to fill. Now you hand defineHost() your catalog, your order
storage, and your completed-order storage, and it builds the rest and wires it up. Three
lines instead of a page of plumbing.
Branded consent pages. new CredentAgent({ branding }) lets the pages your customers see
during a checkout carry your own look instead of the default. The honesty notice on each page
is fixed and cannot be removed or restyled through branding — see the note at the bottom.
Payment credentials work on iPhone. The gate now presents a stable identity to iOS
wallets on the ISO-mdoc path, which is what those wallets require to show a consistent
requester.
Easier credential testing. The tester page now offers QR codes and a raw-certificate
fallback for getting a demo credential onto a phone.
Fixed
Custom credential checks could be skipped, could deadlock, or could silently do nothing.
Three separate gaps in how custom credential rules were enforced. The most serious failed
open — an order could complete without the check it was supposed to require. If you defined
your own credential type, this is the reason to upgrade.
Two different credentials could collide and confuse the wallet. Credential identifiers
were derived from the last part of a document type name. Because these names are conventionally
version-numbered, a payment credential and a loyalty credential could both end up called
"1" — so a request containing both was ambiguous. Identifiers now derive from the full name.
Note this changes the identifier sent to wallets; verification is unaffected because responses
are matched on the credential's own field paths, not the identifier.
Spending grants could be overspent under concurrent use. Two purchases arriving at the
same moment against one grant could both be approved against the same remaining balance.
Spends against a grant are now serialized. Grant amounts are also held as whole cents
throughout, removing the rounding drift that fractional arithmetic introduced.
Checkout receipts now say how payment actually settled instead of implying a settlement
path that may not have run.
The storefront fails immediately when its display bundle is missing, with a message
naming the problem, rather than starting up and failing later at request time.
The Checkout button is reachable on a phone. It could sit off-screen on narrow displays.
Upgrading
npm install @openmobilehub/credentagent-gate@0.4.0 @openmobilehub/credentagent-storefront@0.4.0
Both packages version together — install them as a pair. If you only use the built-in flows,
no code changes are needed.
About trust — please read before deploying
The cryptography on the wire is real: WebAuthn on the passkey flow, and genuine encryption,
nonce binding, and ISO-mdoc parsing on the wallet flows.
But there is still no issuer or device-signature trust anchor. Nothing yet verifies that a
credential was issued by an authority you trust, and the mandate is signed with a development
key. A self-crafted credential would pass. This is why the wallet flows report
trust_level: "presence-only-demo".
A presence-only gate enforces disclosure and binding — that something was presented, and
that it was bound to this exact amount and this exact server. It does not enforce trust.
Do not deploy it as a real safety control, and do not rely on it for an age restriction
that carries legal weight. Issuer-verified trust is the next line of work, not something in
this release.