Cresco — Distributed Identity, Trust & Data-Rights Design¶
Date: 2026-07-02
Status: Phase 0 (identity/sign/trust primitives) + a working slice of Phase 2 (broker tenant
authorization) SHIPPED and PROVEN LIVE in a 2-tenant / 2-region / 1-global federated mesh
(run/tests/tenant_isolation_mesh_test.sh 10/10; security_foundation_test.sh 15/15). Remaining:
mutual-TLS binding of identity at the broker, regional-CA issuance, tenant dataplane namespacing
(§5). All enforcement is behind broker_security_enabled (default OFF → fabric unchanged).
What shipped (this pass)¶
- library
io.cresco.library.security:CrescoIdentity(identity⇄cert-DN),MessageSigner(sign/verify payloads),CertTrust(PKIX chain-verify to trusted region CAs). Pure JDK, additive. - controller:
TenantPolicy(pure ACL logic) +CrescoAuthorizationBroker(ActiveMQ 6.2.7BrokerFilter/BrokerPlugin), wired inActiveBrokerbehindbroker_security_enabled;ActiveClientasserts identity (tenant|region|agent) as the JMS username on network connections. - Proven live: federation bridges form under security (0 self-denial); tenantB is denied tenantA's
namespace at every broker in the mesh (cross-region cross-tenant); each tenant free in its own;
RTT/scaling machinery intact. Local
vm://controller + broker bridges are exempt by design. - Cryptographic identity binding — SHIPPED + PROVEN (single broker). Leaf certificates are now
identity-bearing (
CertificateManagerissuesCN=<agent>, OU=<region>, O=<tenant>), the broker connector takesneedClientAuth(broker_require_client_auth), andCrescoAuthorizationBroker. addConnectionderives the principal from the validated client-cert DN — authoritative, overriding any self-asserted username. Proven live: a client with no certificate is rejected by the handshake; a client presenting a tenantB cert but a spoofedtenantAusername is treated as tenantB (broker log:mTLS identity bound from certificate: CrescoIdentity{tenant=tenantB…}→DENY READ 'tenantA.stream' … outside tenant 'tenantB'). Anti-spoof, end to end. - Fabric-wide mutual TLS via regional CAs — SHIPPED + PROVEN (2026-07-03). The trust-distribution
boundary is closed. Regional-CA enrollment (
security_regional_ca) rides the existing discovery-secret-authenticated cert exchange: the responder (a region/global acting as issuer) signs the joiner a leaf under its CA (RegionCA/CertificateManager.issueEnrollment, region stamps identity) and returns the chain; the joiner installs it (installEnrollment) and trusts the issuing CA that rides inside the chain — so trust is O(regions) (region-CA certs), not O(nodes²). With every node then presenting a CA-signed leaf,broker_require_client_authvalidates any peer by chain. Proven:run/tests/regional_ca_mtls_test.sh10/10 — a global establishes a region CA, two regions (tenantA, tenantB) enroll + install global-CA-signed identities and bridge to the global under mutual TLS, 0 TLS trust failures, healthy federation RTT. (Key fix:ActiveClient.refreshTrustrebuilds only network factories and preservesvm://localhost— closing it dropped the RPC reply consumer and the ping PONG never returned.) Supersedes: the "B-7 broker-auth" item inbroken-and-untouched-report.md(which framed this as a single broker-auth switch). It is not one switch — it is a distributed security fabric.
1. Requirements (as stated)¶
- Tenant isolation — who can see what, who can do what. Data-access rights enforced at the broker.
- Mutual identity — an agent must be able to identify itself and be identified by other agents.
- Trust establishment — nodes must establish trust with each other across the mesh.
- Data rights secured broker-to-broker — protection must survive relaying/bridging across the network, not just the first TLS hop.
- Optional signing / encryption of messages where needed.
- Scale — a huge mesh of interconnected regions and multiple globals. A central CA will not scale; issuance must be distributed, at the regional level.
Mechanisms (cert exchange / signing / encryption) are open — the recommendation below is my judgment.
2. What exists today (code-grounded)¶
- Certs: every node self-generates a 3-tier chain in
CertificateManager.generateCertChain()(controller/.../communication/CertificateManager.java:229):rootCA(self-signed) →intermedCA(signed by rootCA) →endUserCert(leaf, signed by intermedCA), BouncyCastle RSA. The leaf CN is a throwaway UUID (CN=endUserCert-message-<uuid>) — carries no identity. - Trust: peer-to-peer — each node writes its public leaf cert to a shared dir as
<region>_<agent>.cerand imports everyone else's (CertificateManagerexport/import, ~lines 149-197). Identity lives in the filename, not the cert. O(agents²) trust material; no CA relationship. - Transport: ActiveMQ
nio+sslwith the node's chain as key/trust material. Server-auth only —needClientAuthis set nowhere; clients don't present certs. - Connections are anonymous:
createConnection()is called with no credentials (ActiveClient.java:114,189).broker.setUseAuthenticatedPrincipalForJMSXUserID(true)is already set (ActiveBroker.java:283) — waiting for an authenticated principal that never arrives. - Topology: each region runs its own broker; agents are broker clients; region↔global and
global↔global federate via broker-to-broker bridges (
ActiveBroker.AddNetworkConnector). - Destinations: per-agent queue
<region>_<agent>is the one identity-scoped name (the strong isolation point); dataplane topicsagent.event/region.event/global.eventare flat (not scoped). - Enrollment gate already exists: discovery carries
discovery_secret_region/_global— a pre-shared join secret proving a node is allowed into a region/global.
The 3-tier chain, the region-broker/global-bridge topology, the join secret, and the
useAuthenticatedPrincipalForJMSXUserID flag are exactly the primitives this design needs. This is a
restructuring, not a greenfield build.
3. Recommended architecture — Regional CAs + federated trust bundles¶
Three layers, defense-in-depth. Each is independently valuable and separately switch-able.
3.1 Identity — make the certificate self-describing¶
Bind identity into the leaf cert DN instead of a sidecar filename:
Now every cert cryptographically asserts "I am agent A, in region R, tenant T", verifiable by anyone who trusts the issuer. This is the cornerstone — every option below depends on it, and it is Phase-0 additive (subject content doesn't affect the current whole-cert-import trust, so it can't break today's handshake).3.2 Trust — the region is the CA; globals distribute trust, they do not sign¶
The scaling insight: separate issuance from trust distribution.
- Issuance is regional. The regional controller holds a persistent region CA (its
intermedCAtier, promoted from throwaway to a durable signing authority). Agents enroll by submitting a CSR during the existing discovery/registration handshake; the join secret (discovery_secret_region) is the enrollment gate. The region CA signs the agent's leaf (identity in DN) and returns leaf + region-CA cert -
the region's trust bundle. No round-trip to any central authority → issuance scales horizontally, works under partition, and has no single point of compromise.
-
Trust anchors are region CA certs — not leaf certs. A node trusts "any leaf that chains to a region CA in my bundle." Trust material collapses from O(agents) to O(regions you federate with) — one CA cert per region instead of one leaf per agent. This is the key scale win.
-
Globals are trust-list introducers/aggregators, not signers. When region R joins global G, R publishes its (self-signed, identity-bound) region CA cert to G. G maintains the regional trust bundle for its domain and distributes it to member regions. This replaces the fragile per-agent filename exchange with per-region CA distribution.
-
Cross-global mesh = bundle gossip. Globals exchange their regional bundles with peer globals (subject to tenant/trust policy). An agent under G1 can verify an agent under G2 because G1 imported G2's bundle. Federation is bundle-level (a handful of CA certs per global), so it scales with the number of federating domains, not nodes.
-
Optional fabric root (opt-in). Deployments wanting a single cryptographic anchor can have region CAs cross-signed by an offline fabric root. Then trust = chain-to-fabric-root and bundle distribution becomes an optimization rather than a requirement. The root signs region CAs at region birth only (not per agent), so it is never a runtime bottleneck. Kept optional so the default stays fully distributed.
Why not the alternatives: - Central CA / global-signs-everything — the user's rejected case: per-agent enrollment round-trips to global, global compromise = total compromise, global is an enrollment SPOF. ✗ - Pure per-agent web-of-trust (today's model) — O(agents²) cert exchange; does not scale to a huge mesh. ✗ - Regional CAs + federated bundles (this) — regional issuance + O(regions) trust + optional root. ✓
3.3 Data rights — enforce at every broker, and end-to-end when needed¶
-
Transport (default): mutual TLS. Turn on
needClientAuthso the broker requires a client cert, validates it chains to a trusted region CA, and extracts the DN → authenticated principal. WithuseAuthenticatedPrincipalForJMSXUserID(true)(already on), every message is stamped with a verified origin identity the broker trusts. -
Authorization:
CrescoAuthorizationBroker— an ActiveMQBrokerFilterplugin that checks the authenticated principal (tenant/region/agent parsed from the DN) against destination ACLs onaddConsumer/addProducer/send: - an agent may pub/sub only its own queue
<region>_<agent>and its region/tenant-scoped topics; - cross-tenant destinations → deny;
-
vm://localhost(the controller's own in-JVM connection) → full local rights (bounds blast radius: the local agent keeps working; only network clients are constrained). -
Tenant isolation on shared topics (Phase 3): namespace destinations by tenant —
T<tenant>.region.event,T<tenant>.<region>_<agent>. ACLs and bridges become simple prefix rules; a bridge only forwards its tenant namespace, so even a shared global broker cannot leak across tenants. -
End-to-end across bridges (the "broker-to-broker" requirement): selective message signing. Transport mTLS protects one hop; a control/data message that transits region→global→region crosses multiple broker trust domains. For those, the sender signs the payload digest with its leaf private key; any receiver verifies with the sender's leaf cert (chain-validated to a trusted region CA). The signature proves origin identity and tamper-evidence independent of how many brokers relayed it. Selective (per-message flag) because signing every message is costly — mTLS is the default, signing is the elevated tier for cross-domain / sensitive traffic.
-
Confidentiality beyond transport (optional): payload encryption to the recipient's public key (from their leaf cert) for messages that must stay opaque to an intermediate broker. Expensive; use selectively.
4. Lifecycle at scale¶
- Enrollment: agent generates keypair → CSR(identity) → submits to region CA over the existing discovery handshake, gated by the join secret → receives signed leaf + region CA cert + trust bundle.
- Revocation by expiry: short-lived leaves (hours/days) + auto re-enrollment. A compromised node simply isn't renewed; access lapses fast — no mesh-wide CRL/OCSP to scale. A small region-published revocation delta rides along in the bundle for immediate kills.
- Rotation: region CA rollover overlaps old+new CA certs in the bundle; leaf rotation is just re-enrollment. Fabric-root (if used) signs region CAs rarely and offline.
5. Phasing (nothing below is flipped on a live mesh blind — each is flag-gated, staged region-by-region)¶
| Phase | Deliverable | Risk | Flag |
|---|---|---|---|
| 0 ✅ DONE | Identity-bearing leaf DN; io.cresco.library.security identity/chain-verify + sign/verify primitive |
additive, none | n/a |
| 2 ✅ SHIPPED | CrescoAuthorizationBroker tenant ACLs (proven in federated mesh) + broker mutual-TLS needClientAuth cert-DN binding (proven single-broker) |
high (fabric) | broker_security_enabled, broker_require_client_auth |
| 1 ✅ SHIPPED | Region-CA issuance at enrollment (over the discovery-secret exchange) + chain-based trust — fabric-wide mutual TLS proven (regional_ca_mtls_test.sh 10/10) |
medium | security_regional_ca |
| 3 ← NEXT | Tenant destination namespacing + bridge tenant filtering (multi-tenant dataplane isolation); cross-region region-CA bundle distribution (global→regions, global↔global) | high | tenant_namespacing |
| 4 | Revocation/rotation automation; selective payload encryption | medium | per-feature |
Phase 2 is the fabric-breaking one — mТLS + ACLs mean a misconfigured principal/ACL denies real
traffic. It ships only after Phase 1 gives every node an identity-bound cert chaining to a distributed
region CA, and it is enabled per-region behind broker_security_enabled with the local vm:// path always
allowed, so a bad rule can never lock a controller out of its own broker.
6. Test matrix (per phase, before enable-by-default)¶
- Identity: leaf DN parses to correct tenant/region/agent; peer cert chain-verifies to region CA; fails for a cert from an untrusted region.
- Signing: verify passes for untampered payload, fails on a flipped bit or wrong signer.
- Enrollment: agent with valid join secret gets a chaining cert; wrong/missing secret is refused.
- Authz: agent reads/writes its own queue; denied on another agent's queue and a cross-tenant topic;
local
vm://retains full rights; region bridge still federates. - Scale/federation: region joins a global and its CA propagates; two globals exchange bundles; an agent under G1 verifies an agent under G2; revoked/expired leaf is rejected fabric-wide within the TTL window.
- Regression: the full existing bring-up/recovery suite stays green with each flag off (default) and, separately, on.