The /about reality table marks Trust list governance as draft
because v1 ships a working but deliberately-stubbed model:
owner-managed, off-chain-enforced, single-principal. This document is
the design spec for the v2 DAO replacement so the row can flip to
real when the on-chain governance ships.
Compass receipts only mean something if the eligibility-check evaluated a credential signed by an issuer Compass trusts. The trust list is the cryptographic boundary between "Maria's HELP-issued FDH credential is valid" and "anyone with an Ed25519 keypair can self-issue a credential that says is_FDH_in_HK = true."
Two attack surfaces the trust list closes:
- Self-issued credentials. Without a trust list, an attacker mints
their own SD-JWT VC claiming any predicate they like, presents it to
the enclave, and the enclave evaluates it as
eligible: true. - Compromised issuer keys. If HELP's signing key leaks, every credential it signed is forgeable until the key is rotated. The trust list needs revocation pathways that are faster than the credential expiry window.
| Component | Where | Behavior |
|---|---|---|
| Per-agent trust root | AgentRegistry.Agent.trustListRoot (bytes32) |
Stored on-chain; v1 leaves it bytes32(0) (no enforcement) |
| Per-policy trusted issuers | CompassPolicy.trustedIssuers (string[]) |
Off-chain in docs/policies/<slug>.json; baked into the policy hash |
| Enforcement | enclave/src/verifier.ts |
Off-chain check that SD-JWT VC's iss claim ∈ policy.trustedIssuers |
| Issuer addition | docs/policies/*.json edit + redeploy |
Owner-managed; whoever has commit access controls the trust list |
| Issuer revocation | Status list at enclave/src/trust.ts |
Per-credential revocation via IETF jwt-status-list draft |
v1 limits documented in docs/honest-limits.md:
- The repo owner has unilateral authority to add/remove trusted issuers.
- Revocation latency is bounded by status-list polling cadence (default 1hr).
- Cross-issuer threshold trust ("a credential needs 2-of-3 NGOs to co-sign") is not supported.
| Threat | v1 mitigation | v2 must close |
|---|---|---|
| Self-issued credential | Off-chain check against fixed trustedIssuers list |
Same, but list is on-chain + governed |
| Issuer key compromise | Manual docs/policies/*.json edit + redeploy |
Multi-sig revocation tx, ≤ 24h propagation |
| Repo owner goes rogue | Cannot fix in v1 | DAO threshold required to add/remove issuers |
| Sybil issuer (attacker spins up an issuer DID + lobbies for inclusion) | Manual review by owner | DAO vote with public discussion period |
| Issuer drift (legitimate issuer changes keys) | Manual trustedIssuers update + redeploy |
On-chain rotation primitive (key replacement, not issuer replacement) |
struct TrustedIssuer {
bytes32 issuerDidHash; // keccak256 of did:key:z6Mk...
address rotationKey; // can rotate the issuer's signing key
uint64 addedAt;
uint64 revokedAt; // 0 if active
bytes32 metadataURI; // IPFS pointer to issuer name + audit
}
mapping(bytes32 => TrustedIssuer) public issuers;State transitions:
- Add:
proposeAddIssuer(issuerDidHash, rotationKey, metadataURI)→ 7-day timelock →executeAddIssuer(...)requires DAO threshold signatures. - Revoke:
proposeRevoke(issuerDidHash)→ 24-hour expedited timelock →executeRevoke(...)requires DAO threshold OR a single multi-sig "incident response" key (smaller threshold for speed at the cost of needing post-hoc DAO ratification). - Key rotation:
rotate(issuerDidHash, newDidHash, sig)requires signature fromrotationKey. No DAO involvement — the issuer manages their own keys.
- 5 seats minimum, 7 recommended. Compass v2 starts with:
- 1 seat per partnered NGO (HELP, Bethune, Bethune House, MFMW)
- 1 seat for Compass core (governance bootstrap, exits after year 1)
- 1 seat for an external auditor (rotates yearly)
- 1 seat for community-elected migrant-rights advocate
- Quorum: 4-of-7 for adds, 3-of-7 for revokes (faster revocation).
- Voting period: 7 days for adds (public discussion + Telegram digest). 24 hours for revokes.
v1 stores trust on the AGENT (AgentRegistry.Agent.trustListRoot).
That model is wrong for v2:
- Maria shouldn't need to maintain her own trust list — she's not the expert on which NGOs are legitimate.
- A global trust list governed by the DAO is what Compass's privacy promise actually rests on.
v2 migration: the per-agent trustListRoot field is repurposed
or deprecated. New deployments leave it bytes32(0); the off-chain
verifier reads from the global on-chain list instead. v1 deployments
remain functional but their trustListRoot field becomes purely
informational.
A natural extension is "this credential needs to be co-signed by 2 of 3 trusted issuers" — defends against a single compromised NGO. v3 work; out of v2 scope. v2 ships single-issuer trust to keep the governance complexity bounded.
- Deploy
TrustListcontract on Aristotle mainnet (alongside AgentRegistry + CompassHub redeploy). Bootstrap with the 4 NGOs already named indocs/policies/*.json. - Update
enclave/src/verifier.tsto read trusted issuers from the on-chain contract via RPC. Cache for 5 minutes; force-refresh onIssuerRevokedevents seen via WebSocket subscription. - Deprecate the
trustedIssuersfield in policy JSON. Policies continue to declare expected VCT but no longer carry their own trust list. - Publish DAO charter at
docs/governance/charter.md(TBD). - Run a 30-day v1+v2 dual-write period where both the JSON list and the on-chain contract are honored. v1 list is authoritative if they disagree (rollback safety). After 30 days, flip to on-chain authoritative.
- Who pays gas for issuer additions? DAO treasury vs the issuer themselves. v2 sketch: issuer pays. Removes a perverse incentive.
- Can a revoked issuer's prior receipts still be verified? Yes —
the receipt was signed by an issuer that WAS trusted at the time of
signing. A separate "stale receipt" check uses the receipt's
expiryfield; revocation does not retroactively invalidate prior receipts. - What about issuer-side credential revocation? Already handled
separately by
enclave/src/trust.ts+ the IETF jwt-status-list draft (per-credential revocation). The v2 trust-list governance is about issuer-level addition/removal, not per-credential revocation.
If the v1 owner-managed model is exploited (e.g., Stephen's GitHub
account is compromised), the only mitigation is to revert the
malicious docs/policies/*.json change. The Vercel deploy follows
main so the next push fixes it; in the worst case, Vercel rollback
is a 1-click operation. v2 DAO governance closes this entirely.
- Per-agent
trustListRootdeclaration:contracts/AgentRegistry.sol:19 - Off-chain enforcement:
enclave/src/verifier.ts - Per-policy
trustedIssuers:enclave/src/types.ts:53 - Per-credential revocation (separate concern):
enclave/src/trust.ts - IETF OAuth Token Status List (draft): https://datatracker.ietf.org/doc/draft-ietf-oauth-status-list/
- Honest limits item 7 (trust list governance v1):
docs/honest-limits.md