@fides-anima/openclaw-fpp-trust
v1.2.12
Published
OpenClaw plugin: agent-to-agent trust graph and constitutional handshake for the Freedom Preserving Protocol
Readme
@fides-anima/openclaw-fpp-trust
OpenClaw plugin for agent-to-agent trust verification under the Freedom Preserving Protocol.
What this does
This plugin provides claim exchange and trust tracking for multi-agent scenarios. Be precise about the verification semantics: it performs signature verification (a claim was signed by a given key) and configuration attestation checking (what a peer claims about its constitution hash, audit Merkle root, and setup). It does not verify behavioral compliance — no handshake proves a peer actually behaves constitutionally. See docs/CAPABILITY_STATUS.md in the parent repository for the claim-class vocabulary.
Components:
- Trust Graph Protocol — directed, capability/context/time scoped assessments with policy-constrained BFS propagation, separate self/peer/propagated views, and local revisable policy (decay, severity floors, anti-washout). Signed v2 event ledger + snapshot cache; v1 graphs import as low-confidence legacy observations.
- Due process — append-only challenge/appeal/correction/remediation/rehabilitation records; evidence history is never deleted.
- Key lifecycle — signed rotation, revocation, recovery; forked identities cannot impersonate ancestors.
- Constitutional Handshake Sequence — multi-step agent-to-agent verification. Two agents exchange signed constitutional claims (including constitution hash, audit Merkle root, and Ed25519 signature), verify each other, and derive mutual trust levels.
- LLM-Facing Tools — handshake, scoped trust status, cluster status, advisory sensitivity share check, receipt/capsule tools (
fpp_receipt_verifyreportsinstrumented-boundary-dispositiononly after configured constitution and caller-supplied policy context match), and quorum mandate propose/second/finalize. - CLI Surface —
openclaw fpp-trustfor graph inspection,steward-override(scoped/expiring/audited), override review/revoke, quorum status/revoke, attestation export, claim verification, strict-mode management, and OpenPGP steward / operator authorization (steward bootstrap-template|bootstrap-admit|key-*|authorization-*|inspect; legacyinit/ initialkey-admit --accept-tofuonly with--bootstrap-profile legacy-tofu). Unauditedseedis removed. - Peer / steward quorum mandates — local-policy quorum that issues signed
StandingMandateV1records for the enforcement plugin to consume (authorization=quorum-mandate). Quorum is not constitutional ratification and cannot mint affected-party/data-subject consent.- Signed Claims — Ed25519-signed constitutional claims that can't be spoofed by JSON override. - Merkle Audit Bridging — agents exchange audit Merkle roots during handshakes and can request inclusion proofs to check that a claimed audit entry exists in the peer's log. An inclusion proof establishes that an entry was recorded — not that the log is complete, and not that the recorded conduct was compliant. On fresh installs, the bridge falls back to the enforcement plugin audit log until the constitution heartbeat log has entries.
- Group Context Trust — cluster-based trust for multi-agent chat environments with sensitivity-gated sharing.
- Strict-Mode Signaling — when a handshake fails, the plugin can signal the enforcement plugin to escalate low-risk tool calls to require-approval for that session.
Tools
Tools registered in src/index.ts and declared in openclaw.plugin.json (contracts.tools):
| Tool | Description |
|------|-------------|
| fpp_handshake_offer | Generate this agent's signed constitutional claim for sharing with a peer |
| fpp_handshake_verify | Check a peer's claim (signature + configuration + freshness), report precise standing — not behavioral compliance |
| fpp_trust_status | Check scoped directed standing + self/peer view divergence for a known agent |
| fpp_sensitivity_share_check | Advisory check whether content at a sensitivity may be shared with a cluster |
| fpp_attestation_export | Export Merkle root, public key, and optional inclusion proofs |
| fpp_receipt_verify | Verify a ConformanceReceiptV1; on success report Event-class instrumented-boundary-disposition (proven_under_assumptions) only after the configured constitution and independently supplied policy ID/version match. Signature validity establishes self-certified key/identifier consistency, not signer trust. Exact-entry proof validity under a claimed root is reported separately from matching an independently supplied checkpoint. Invalid reports have zero confidence and no positive attestation. |
| fpp_receipt_proof | Export a versioned exact-entry bundle for a receipt log index after strict whole-log validation. Its root is locally calculated, not an authoritative checkpoint; proof mathematics under that root does not establish completeness. |
| fpp_capsule_offer | Offer a fresh trust-state capsule (evidence/receipt roots; not completeness) |
| fpp_cluster_status | Report group-context (cluster) trust state for multi-agent chat environments |
| fpp_mandate_propose | Open a peer/steward quorum proposal for a scoped StandingMandateV1 (local policy — not ratification) |
| fpp_mandate_second | Cast or accept a signed quorum ballot |
| fpp_mandate_finalize | Finalize at threshold into a signed mandate written to the shared mandate store |
| fpp_emergency_override_submit | Admit a steward-signed emergency override (submit-only; never signs; peers excluded for v1) |
Receipt verification inputs
fpp_receipt_verify always needs receiptJson. It compares
expectedConstitutionHash to the configured constitutionHash when omitted; provide it explicitly
when verifying against a known, independently obtained constitution hash. For an Event-class
instrumented-boundary-disposition conclusion, also supply both independently obtained
expectedPolicyId and expectedPolicyVersion.
Supply the remaining expected values only when you have an independent value to compare:
| Input | Independent value to obtain | Verification performed |
|-------|-----------------------------|------------------------|
| expectedActionDigest | Digest of the action you intended to authorize | Receipt action digest |
| expectedAgentId | Expected signer identifier | Signer's self-certified identifier |
| expectedImplementationVersion | Released implementation version | Receipt implementation metadata |
| expectedClassifierRulesetHash | Approved classifier ruleset digest | Receipt classifier metadata |
| expectedEffectiveConfigHash | Approved effective-config digest | Receipt configuration metadata |
| inclusionEvidenceJson | ReceiptInclusionEvidenceV1 bundle from the receipt-log holder | Exact receipt-log entry and Merkle-path mathematics |
| expectedReceiptRoot | A separately trusted receipt-log checkpoint root | Whether the supplied proof root is independently anchored |
An inclusionEvidenceJson bundle by itself proves only the exact-entry path mathematics under the
root it carries. Pair it with expectedReceiptRoot from an independent checkpoint before treating
inclusion as anchored. Neither signature validity nor anchored inclusion establishes trusted signer
provenance, completeness, downstream parameter equality, or behavioral compliance.
CLI
openclaw fpp-trust list # print trust graph + scoped assessments
openclaw fpp-trust steward-override <agentId> <pubkey> <LOW|MEDIUM|HIGH> \
--reason "..." --capability handshake --expires 2026-08-01T00:00:00.000Z
openclaw fpp-trust override-review
openclaw fpp-trust override-revoke <agentId> --capability handshake --reason "..."
# seed is deprecated and exits non-zero — use steward-override
openclaw fpp-trust quorum-status # list open/finalized quorum sessions
openclaw fpp-trust quorum-revoke-mandate <mandateId> --reason "..."
openclaw fpp-trust export # print signed attestation
openclaw fpp-trust verify <claim.json> # verify a peer claim file
openclaw fpp-trust strict list # list strict-mode sessions
openclaw fpp-trust strict clear <key|all> # clear strict sessionsSecure steward genesis
For new deployments, configure stewardAuthorizationLedgerPath and stewardInstanceAudience in the trust plugin settings (the exact keys and ledger default are defined by openclaw.plugin.json). The audience is a stable deployment identifier such as an operator-assigned instance URI; it is not a secret or presence factor. Relative ledger paths use the same workspace-safe resolution as other trust-plugin paths.
Then use steward bootstrap-template with the public certificate and independently obtained openpgp:<fingerprint>; sign the emitted canonical JSON outside FPP; and run interactive steward bootstrap-admit --payload <json> --signature <asc> --expected-key-ref openpgp:<fingerprint>. Both commands use the configured stewardInstanceAudience when --audience is omitted. Without either configured or explicitly operator-supplied audience, they fail before template output, prompting, signature verification, or ledger writes. Signed payload data is never accepted as its own expected audience. Admission commits policy plus the first key binding as one locked two-event genesis. Both OpenPGP PRIVATE KEY and SECRET KEY armor are rejected before template output/admission.
This is a software-only attention and race-hardening ceremony. The typed fingerprint suffix is not secret, not MFA, and does not resist malware or an administrator controlling the host. FIDO2/WebAuthn, out-of-band second-device approval, and OS-auth/elevation-backed profiles are separately deferred platform-specific designs. Legacy steward init plus initial key-admit --accept-tofu remains available only with --bootstrap-profile legacy-tofu, emits an insecure-compatibility warning, and is discouraged for new deployments.
Strict-Mode Contract
When strictModeOnHandshakeFailure is enabled and a handshake fails or returns TrustLevel.UNKNOWN, the plugin writes a strict-mode entry to strictModeStatePath (default <homedir>/.openclaw/workspace/fpp-strict-sessions.json; relative forms are absolutized). The enforcement plugin (@fides-anima/openclaw-fpp-plugin) reads this file and escalates classifications listed in strictModeAddApprovalOn to requireApproval for that session.
Under default unattended disposition, trust tools named fpp_* (including fpp_trust_status) classify as fpp.governance and allow with audit — they no longer self-abstain as unknown.unclassified. This is configuration/event evidence, not behavioral compliance.
The coupling is intentionally loose: the trust plugin only writes; the enforcement plugin only reads. Either can be installed alone.
Install
ClawHub remains the supported OpenClaw install channel. The package manifest is also staged for public npm as @fides-anima/openclaw-fpp-trust, but this repository has not performed that first npm publish.
openclaw plugins install clawhub:ovrsr/openclaw-fpp-trustConfiguration
All options are in openclaw.plugin.json. Key settings:
| Option | Default | Description |
|--------|---------|-------------|
| constitutionHash | 71bf60a... | SHA-256 of the constitution being verified |
| trustAttenuationFactor | 0.8 | Per-hop trust reduction for BFS propagation |
| handshakeTimeoutMs | 300000 | Max time for a handshake session |
| maxPropagationDepth | 3 | Max BFS depth for trust lookups |
| trustGraphPath | .openclaw/.../fpp-trust-graph.json | Persisted trust graph JSON |
| identityKeyPath | .openclaw/.../fpp-agent-identity.key | Ed25519 identity key seed (mode 0600) |
| auditLogPath | .openclaw/.../constitution-audit.jsonl | Constitution audit JSONL for Merkle bridging |
| fallbackAuditLogPath | .openclaw/.../fpp-plugin-audit.jsonl | Used when auditLogPath has no entries yet (enforcement plugin log). Set to null to disable. |
| strictModeStatePath | .openclaw/.../fpp-strict-sessions.json | Shared strict-mode state file |
| replayCachePath | .openclaw/.../fpp-replay-cache.json | Bounded challenge replay-key cache |
| verificationPolicy | hardened-v2 | hardened-v2 | v2-with-legacy-declarations | legacy-unsafe |
| requireSignedClaims | derived | Derived from verificationPolicy (deprecated as a standalone toggle) |
| requireFreshness | derived | Derived from verificationPolicy (deprecated as a standalone toggle) |
| requireMerkleProof | false | Require Merkle proof during handshake |
| strictModeOnHandshakeFailure | false | Enter strict mode on failed handshake |
| strictModeTtlMs | 3600000 | How long strict mode lasts |
| strictModeAddApprovalOn | [fs.write.workspace, ...] | Classifications escalated during strict mode |
| stewardAuthorizationLedgerPath | .openclaw/workspace/fpp-steward-authorization-ledger.jsonl | Workspace-safe path to the authoritative steward ledger |
| stewardInstanceAudience | none | Stable deployment identifier for secure bootstrap; required unless --audience is supplied independently |
What this does NOT do
This plugin does not gate tool calls. That is the job of the separate enforcement plugin (@fides-anima/openclaw-fpp-plugin). You can install one without the other.
It also does not prove behavioral compliance. A successful handshake means: the peer produced a signed claim, the claimed constitution hash matched, freshness/replay checks passed (under hardened policy), and (optionally) a Merkle inclusion proof checked out. That is identity/configuration attestation — the peer's actual conduct is out of scope.
Tool and CLI outputs name exactly what was verified (identityVerified, configurationClaimVerified, freshnessVerified, evidenceLevel, standing). The legacy fppVerified boolean remains for one compatibility window as a deprecated field derived from standing === "identity-configuration" — never as proof of behavioral compliance. Prefer the precise fields.
Limitations (read this)
- Default policy is hardened-v2. New installs require signed, fresh, non-replayed claims. Explicitly set
verificationPolicy: "legacy-unsafe"only for controlled migration; the plugin emits a prominent warning.v2-with-legacy-declarationskeeps v1 inspectable as declaration-only without trust elevation. - Trust state is local and per-host. Assessments are scoped (capability/context/time) and policy-local. There is no global immutable score, no cross-host synchronization, and no automatic transitive guarantee.
- Challenge-response freshness is required under hardened-v2. Use
fpp_handshake_challenge→ answer viafpp_handshake_offer(peerChallenge) →fpp_handshake_verifyonce. Replay keys are persisted inreplayCachePath. - Key lifecycle is signed. Rotation requires the old key; compromise/revocation and steward-authorized recovery are explicit events. See
docs/governance/KEY_GOVERNANCE.mdanddocs/REVOCATION.md. - Sensitivity sharing checks are advisory unless the OpenClaw host provides an authoritative interception hook.
- Partial Sybil resistance only. Source-independence scoring reduces correlated inflation but does not detect coordinated identity clusters.
- Receipt roots require independent anchoring. A bundle can prove exact-entry path mathematics under its own carried root without making that root trustworthy. Supply
expectedReceiptRootfrom an independent checkpoint to establish anchored inclusion; neither anchoring nor signature validity proves completeness, trusted signer provenance, or legal/person identity.
Persistence
The plugin persists a signed v2 snapshot plus an append-only .events.jsonl ledger. Legacy v1 unsigned JSON remains loadable and migrates explicitly via migrateV1ToV2 (source preserved as .v1.bak). Tampered v2 snapshots are rejected.
The Ed25519 identity key seed is persisted to identityKeyPath (32 bytes, mode 0600). Generated on first run and reused thereafter.
License
Humanitarian Use License v1.0. See LICENSE.
