Chio/Docs
LOGIN · JOIN

Reputation Clearing

Combine what several partners report about one subject into one admission class, weighed under your policy and capped per issuer.

Concept chio@5f63e6e

On this page

An operator can admit a subject on what several partners report about it, without letting any one partner decide. The receiver weighs every report under its own policy, caps what each issuer contributes, and needs two independent issuers before a negative event blocks.

A federated reputation clearing (chio.federation-reputation-clearing.v1, FederatedReputationClearingArtifact) records that decision for one subject. The published reference clearing, frc-1, weighs four inputs from three issuers and yields reviewable.

One clearing, several issuersChoose an outcome.
Their operatorsYour operatorσoriginσmirrorσindexerπyour clearingκadmission classreviewableTheir operatorsYour operatorσoriginσmirrorσindexerπyour clearingκadmission classreviewable

λlocal weighting requiredmat most 2 inputs per issuerwno weight above 4000 bpssone accepted summary per issuernat least 2 independent issuersνblocking events corroborated

Four inputs from three issuers clear as reviewable

Origin and mirror each contribute one summary, at 3000 and 2500 bps. The indexer's blocking negative event, at 2000 bps, is corroborated by origin's at 1500 bps. No input exceeds the 4000 bps oracle cap, and the clearing yields reviewable under admission policy foap-1. Reference clearing.

The reference clearing frc-1: four inputs from three issuers with their local weights in basis points, the six sybil checks in the validator's order, and the class the clearing yields. Expiry windows, independence groups other than the issuer, and how the referenced summaries are fetched are left out.

Every input names its issuer, and the receiver sets its weight

An input is a reputation summary or a negative event about the clearing's subject. It references a portable summary or a portable negative event by digest, names the issuing operator, and carries the receiver's weight for it in basis points, from 1 to 10000.

The clearing lists the operators party to it, and every input's issuer must be one of them. It names the receiver's local weighting policy and the open-admission policy its class is drawn from, and it sorts every input into exactly one of accepted or rejected. Only accepted inputs count toward the sybil control.

The sybil control caps what any one issuer can contribute

Five settings bound the clearing, and the validator refuses any clearing that breaks one. The reference clearing's values are in parentheses.

  1. Local weighting. localWeightingRequired is true on every valid clearing, so the receiver's own weights always apply.
  2. Inputs per issuer. One issuer contributes at most maximumInputsPerIssuer inputs (2), and at most one accepted reputation summary.
  3. Oracle cap. No input's weight exceeds oracleCapBps (4000), so no single issuer becomes the oracle.
  4. Independent issuers. Accepted inputs come from at least minimumIndependentIssuers independence groups (2).
  5. Corroboration. With negativeEventCorroborationRequired, a blocking negative event needs a second, independent issuer.

An input's independence group is its issuerIndependenceGroupId when it names one, and its issuer otherwise, so two issuers in one group count once. A clearing that accepts no input skips the minimum, and its class is then public_untrusted.

A blocking negative event stands only with a second issuer behind it

A negative event marked blocking can keep a subject out. The validator groups accepted blocking events by independence group and refuses the clearing when they all come from one group.

In frc-1, indexer-operator-a and origin-operator each report a blocking event, so the pair stands. Only negative events block: a reputation summary marked blocking is refused. Corroboration is requirement TRUSTMAX-04 in the qualification matrix on Federation Assurance.

The federation guide clears frc-1 and refuses three variants of it

The guide project validates the published reference clearing, then hands the same validator a clearing with two accepted summaries from one issuer, one without local weighting, and one under an unsupported schema.

Clear shared reputation under local weighting

Validate the reference clearing, then refuse a clearing with two accepted summaries from one issuer and a clearing that drops local weighting.

Complete the project setup, then run the program from federation-guide/. The download includes every constructor and input.

Terminalbash
cargo run --locked --bin reputation
Output on darwin/arm64 · exit 0
reference clearing frc-1: valid; 4 inputs from 4 operators; effective class Reviewable
sybil control: at least 2 independent issuers, at most 2 inputs per issuer, oracle cap 4000 bps
two accepted summaries from one issuer: refused (invalid clearing: accepted reputation summaries must come from distinct issuers)
clearing without local weighting: refused (invalid clearing: federated reputation clearing must require local weighting)
unsupported schema: refused (unsupported schema: chio.federation-reputation-clearing.v0)

Clearing checks the contract over declared inputs; it does not fetch or verify the referenced summaries and negative events. Receiver-side scoring is a separate local policy, documented under Economy.

Read the complete program 49 lines · complete file
src/bin/reputation.rsrust
//! Validate a shared reputation clearing and refuse the shapes that would turn
//! it into an oracle: two accepted summaries from one issuer, and clearing
//! without local weighting.
use chio_federation::error::FederationContractError;
use chio_federation::reputation::{
    validate_federated_reputation_clearing, FederatedReputationClearingArtifact,
};
use serde_json::Value;

fn receive(value: Value) -> Result<FederatedReputationClearingArtifact, FederationContractError> {
    let clearing: FederatedReputationClearingArtifact = serde_json::from_value(value)
        .map_err(|e| FederationContractError::InvalidClearing(e.to_string()))?;
    validate_federated_reputation_clearing(&clearing)?;
    Ok(clearing)
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let reference: Value = serde_json::from_slice(&std::fs::read("reputation-clearing.json")?)?;
    let clearing = receive(reference.clone())?;
    println!(
        "reference clearing {}: valid; {} inputs from {} operators; effective class {:?}",
        clearing.clearing_id,
        clearing.inputs.len(),
        clearing.participating_operator_ids.len(),
        clearing.effective_admission_class
    );
    println!(
        "sybil control: at least {} independent issuers, at most {} inputs per issuer, oracle cap {} bps",
        clearing.sybil_control.minimum_independent_issuers,
        clearing.sybil_control.maximum_inputs_per_issuer,
        clearing.sybil_control.oracle_cap_bps
    );
    let mut duplicate = reference.clone();
    duplicate["inputs"][1]["issuerOperatorId"] = "origin-operator".into();
    duplicate["inputs"][1]["artifactRef"]["operatorId"] = "origin-operator".into();
    let err = receive(duplicate).unwrap_err();
    println!("two accepted summaries from one issuer: refused ({err})");
    let mut unweighted = reference.clone();
    unweighted["sybilControl"]["localWeightingRequired"] = false.into();
    let err = receive(unweighted).unwrap_err();
    assert!(matches!(err, FederationContractError::InvalidClearing(_)));
    println!("clearing without local weighting: refused ({err})");
    let mut wrong_schema = reference;
    wrong_schema["schema"] = "chio.federation-reputation-clearing.v0".into();
    let err = receive(wrong_schema).unwrap_err();
    assert!(matches!(err, FederationContractError::UnsupportedSchema(_)));
    println!("unsupported schema: refused ({err})");
    Ok(())
}

Open the source file. Each program asserts its expected refusals; keys, timestamps, and digests change between runs.

You should see frc-1 valid with effective class Reviewable and the sybil control read back, then three refusals, each naming the rule or schema that failed.

Reputation from outside a clearing arrives as evidence you weigh

When your operator imports a partner's evidence package, the local comparison view reports native reputation and imported trust side by side. It shows explicit provenance and echoes the imported-trust policy at report time; the share's import-time requireProofs flag rides in its provenance. By default, proofless shares are rejected, stale signals expire, and the composite score is attenuated.

An Agent Passport carries issuer-signed credentials with identity and reputation information, including references to receipt evidence. Your operator checks the credential signatures and applies its own issuer, status, and evidence policy. A referenced receipt root identifies evidence, and each receipt beneath it is verified on its own. Compare a presented passport with your own receipts for the same subject, and the CLI prints both scores and the attenuation between them:

Terminalbash
chio reputation compare --subject-public-key 5a2d…e7c4   --passport passport-halden-bounty-417.json
Output
subject     5a2d…e7c4 (fixer-2)
passport    passport-halden-bounty-417, issuer did:chio:z6MkpA7…c2T, signatures verify
native      0.71 composite over 38 receipts on your kernel
imported    0.84 claimed, 0.42 after 0.50 attenuation, proofs required
policy      verifier policy satisfied; the passport authorizes no call
  • Issuance. A passport feeds the verifier policy that decides whether the trust service issues a capability for its subject (chio trust federated-issue). A presented passport authorizes no tool call itself, and the federated grant it leads to requires a DPoP proof at your door.
  • Several issuers. The bounded CrossIssuerPortfolio profile composes credentials from several issuers as an evidence container, and adds no trust root of its own.
  • Lifecycle. Status is issuer-signed, pulled from the status resolve URL, and kept for its TTL; a stale status fails closed. When verifier policy requires an active lifecycle, stale, superseded, revoked, and not-found refuse the presentation.

Read more in Portable reputation and Reputation and passports.

Clearing fields and refusals

FederatedReputationClearingArtifact

Field names are as they serialize. The types are defined in crates/trust/chio-federation/src/reputation.rs and validated there and in crates/trust/chio-federation/src/validation.rs.

FieldHoldsRule
schema
schema id
chio.federation-reputation-clearing.v1
clearingId
id
non-empty
generatedAt
Unix seconds
when the clearing was computed
subjectKey
subject
every input targets it
namespace
registry namespace
non-empty
participatingOperatorIds
list
non-empty, unique; holds every issuer
localWeightingPolicyRef
policy reference
the receiver's weighting policy
admissionPolicyRef
policy reference
the open-admission policy the class is drawn from
inputs[]
list
non-empty; ids unique
sybilControl
object
the control below
acceptedInputIds, rejectedInputIds
lists
every input in exactly one
effectiveAdmissionClass
enum
public_untrusted, reviewable, bond_backed, role_gated
continuity
object, optional
continuityId, optional previousClearingId unequal to clearingId
note
text, optional
free text

FederatedReputationInputReference

FieldHoldsRule
kind
enum
reputation_summary or negative_event
artifactRef
artifact reference
a portable summary or negative event to match kind; its operatorId is the issuer
subjectKey
subject
equals the clearing's
issuerOperatorId
operator id
who issued the input
issuerIndependenceGroupId
id, optional
issuers in one group count once
weightBps
basis points
1 to 10000, at most oracleCapBps
blocking
boolean
negative events only; blocking events group for corroboration
publishedAt, expiresAt
Unix seconds
published by generatedAt; expires after it
note
text, optional
free text

FederatedSybilControl

FieldHoldsRule
minimumIndependentIssuers
integer
non-zero; independence groups among accepted inputs
maximumInputsPerIssuer
integer
non-zero; inputs from one issuer
oracleCapBps
basis points
1 to 10000; the largest single weight
localWeightingRequired
boolean
must be true
negativeEventCorroborationRequired
boolean
blocking events need two independence groups

Validation refusals

A clearing that breaks a sybil, issuer, or classification rule returns FederationContractError::InvalidClearing with one of these messages. Weight ranges and input windows return their own messages in the same variant, and a structural fault returns UnsupportedSchema, MissingField, or DuplicateValue.

  • federated reputation clearing must require local weighting
  • issuer exceeds maximum_inputs_per_issuer
  • input weight_bps exceeds oracle_cap_bps
  • accepted reputation summaries must come from distinct issuers
  • accepted inputs must meet the minimum_independent_issuers threshold
  • blocking negative events require corroboration from independent issuers
  • reputation summaries cannot be marked blocking
  • input issuer_operator_id must appear in participating_operator_ids
  • input artifact_ref operator_id must match issuer_operator_id
  • each input must be classified as accepted or rejected
  • non-public admission classes require accepted inputs

Every federation contract error is indexed on Federation Refusal Codes.

Reference clearing

The published standards example, read from the Chio source and shipped with the federation guide project. The run above validates this file.

docs/standards/CHIO_FEDERATION_REPUTATION_CLEARING_EXAMPLE.jsonjson
{
  "schema": "chio.federation-reputation-clearing.v1",
  "clearingId": "frc-1",
  "generatedAt": 1743552180,
  "subjectKey": "subject-1",
  "namespace": "registry.chio.example/liability",
  "participatingOperatorIds": [
    "origin-operator",
    "mirror-operator-a",
    "indexer-operator-a",
    "consumer-operator"
  ],
  "localWeightingPolicyRef": "policy/reputation/federated-default",
  "admissionPolicyRef": "foap-1",
  "inputs": [
    {
      "kind": "reputation_summary",
      "artifactRef": {
        "kind": "portable_reputation_summary",
        "schema": "chio.portable-reputation-summary.v1",
        "artifactId": "summary-origin-1",
        "operatorId": "origin-operator",
        "sha256": "4444444444444444444444444444444444444444444444444444444444444444",
        "uri": "https://origin-operator.chio.example/artifacts/summary-origin-1"
      },
      "subjectKey": "subject-1",
      "issuerOperatorId": "origin-operator",
      "weightBps": 3000,
      "blocking": false,
      "publishedAt": 1743552000,
      "expiresAt": 1743638400,
      "note": "Origin-issued portable reputation summary."
    },
    {
      "kind": "reputation_summary",
      "artifactRef": {
        "kind": "portable_reputation_summary",
        "schema": "chio.portable-reputation-summary.v1",
        "artifactId": "summary-mirror-a-1",
        "operatorId": "mirror-operator-a",
        "sha256": "5555555555555555555555555555555555555555555555555555555555555555",
        "uri": "https://mirror-operator-a.chio.example/artifacts/summary-mirror-a-1"
      },
      "subjectKey": "subject-1",
      "issuerOperatorId": "mirror-operator-a",
      "weightBps": 2500,
      "blocking": false,
      "publishedAt": 1743552010,
      "expiresAt": 1743638400,
      "note": "Mirror-issued portable reputation summary."
    },
    {
      "kind": "negative_event",
      "artifactRef": {
        "kind": "portable_negative_event",
        "schema": "chio.portable-negative-event.v1",
        "artifactId": "negative-indexer-a-1",
        "operatorId": "indexer-operator-a",
        "sha256": "6666666666666666666666666666666666666666666666666666666666666666",
        "uri": "https://indexer-operator-a.chio.example/artifacts/negative-indexer-a-1"
      },
      "subjectKey": "subject-1",
      "issuerOperatorId": "indexer-operator-a",
      "weightBps": 2000,
      "blocking": true,
      "publishedAt": 1743552020,
      "expiresAt": 1743595200,
      "note": "Indexers contribute corroborated negative-event evidence."
    },
    {
      "kind": "negative_event",
      "artifactRef": {
        "kind": "portable_negative_event",
        "schema": "chio.portable-negative-event.v1",
        "artifactId": "negative-origin-1",
        "operatorId": "origin-operator",
        "sha256": "7777777777777777777777777777777777777777777777777777777777777777",
        "uri": "https://origin-operator.chio.example/artifacts/negative-origin-1"
      },
      "subjectKey": "subject-1",
      "issuerOperatorId": "origin-operator",
      "weightBps": 1500,
      "blocking": true,
      "publishedAt": 1743552025,
      "expiresAt": 1743595200,
      "note": "Independent corroboration keeps a single issuer from becoming a universal oracle."
    }
  ],
  "sybilControl": {
    "minimumIndependentIssuers": 2,
    "maximumInputsPerIssuer": 2,
    "oracleCapBps": 4000,
    "localWeightingRequired": true,
    "negativeEventCorroborationRequired": true
  },
  "acceptedInputIds": [
    "summary-origin-1",
    "summary-mirror-a-1",
    "negative-indexer-a-1",
    "negative-origin-1"
  ],
  "rejectedInputIds": [],
  "effectiveAdmissionClass": "reviewable",
  "note": "Shared reputation clearing preserves local weighting and requires corroborated negative-event inputs."
}

Next: set the policy a clearing's class comes from