The last few sessions assembled ledger’s identity pipeline — DID binding, SCIM lookup, key rotation, VC validation. It worked. It just happened to live in entirely the wrong place.

The question I kept sidestepping was whether ledger actually owns identity or whether it happened to be the first repo that needed it. Once stated plainly, the answer was obvious: ledger’s domain is audit. It cares about who wrote an entry — but resolving that identity isn’t its responsibility. Any module wanting to verify an agent’s identity shouldn’t have to pull in Merkle trees, trust scores, and Flyway migrations to do it.

I formalised this as a platform rule: a module owns infrastructure only if it is the definitive provider of that capability. Development order doesn’t determine ownership.

Phase 1: the pieces with clean boundaries

We moved the pieces with no LedgerEntry in their API surface: the SPIs (ActorDIDProvider, DIDResolver, AgentCredentialValidator), the model types, the CDI events, and all the implementations — no-op defaults, KeyDIDResolver, WebDIDResolver, ScimActorDIDProvider, the cache base. These landed in casehub-platform-identity, a new module in the platform repo.

One constraint appeared during the move. ScimActorDIDProvider had an @Observes AgentKeyRotatedEvent to evict its cache on key rotation. In platform that’s backwards — the platform module can’t observe a ledger event without creating a dependency in the wrong direction. The fix was a thin IdentityCacheInvalidator bean in ledger: it observes the event and calls provider.invalidate(actorId) if the active provider is an AbstractCachingIdentityProvider. The event stays in its owning domain; the domain provides the bridge.

Phase 2: cutting the LedgerEntry dependency

AgentIdentityVerificationService still took a LedgerEntry parameter. The verification logic — resolve the DID document, check alsoKnownAs, match the public key — has nothing to do with ledger. It only needed actorId, actorDid, and agentPublicKey. We put the service in platform with those three primitives and left thin adapter classes in ledger that extract the fields and delegate. Consumers that inject by the ledger type continue to work unchanged.

The build gotcha

After updating deployment/pom.xml to include the new deployment artifacts, the build still failed with “missing dependencies” on the deployment module — even though the source was correct. Claude identified the root cause: the Quarkus extension descriptor validation reads the installed deployment JAR from .m2, not the current source. The error message points at the deployment artifact and says it’s missing deps, which is true of the cached version. Nothing in the error mentions the cache. Fix: install the deployment module first.

Both repos are on main. casehub-platform-identity now provides the SPIs and all standard implementations. Ledger depends on it and wires identity into the audit pipeline via its enrichers. Any future module needing agent identity verification won’t need to touch ledger.


<
Previous Post
Phase 5 Complete: EmulatedGame Accuracy Gaps Closed
>
Next Post
Three layers to one line