KYA Bureau
Reputation for AI agents and the vendors they pay, built on the evidence that fiduciary control produces. Phase 1 runs seven gates before every payment an agent makes. Phase 2, the bureau, rates what those gates observed, signs the rating, and publishes enough of the working that anyone can recompute it.
- Abstract
- Status of this Memo
- Two Phases
- Conformance & Terminology
- Phase 1: Fiduciary Control
- Evidence: Receipts and Keys
- Agent Rating
- Controller Verification
- Gate 5: Human Approval
- Seller Rating
- Recomputability & Endpoints
- Revocation
- Deposits and Slashing
- Federation
- Security Considerations
- Open Problems
- Governance
- References
- Changelog
1Abstract
This document specifies KYA Bureau (“Know Your Agent”), a reputation primitive for autonomous software that spends money on someone else’s behalf, and for the vendors that take that money.
A bureau is only as good as its evidence. The evidence here comes from Phase 1, fiduciary control: before every payment an agent makes, a sequence of gates checks the agent’s runtime, its kill switch, a sanctions list, a vendor allowlist, a per-transaction limit, a daily budget, a human-approval threshold, and its behaviour against its own history. Each decision is recorded, and each clearance can carry an Ed25519-signed receipt. Phase 2, the bureau, rates agents and vendors from that record.
This revision describes the methods Skalor runs in production today, skalor-agent-rating/v1.3 and skalor-seller-rating/v1, with every weight, threshold and formula taken from the running code. Each mechanism is labelled Live (running in production) or Proposed (specified here, not built). Where v0.2 described something that is not built, this revision either corrects the text or moves the item to Open Problems (§16).
2Status of this Memo
This is a draft specification published for public comment. It is not a final standard. Implementations following this draft should be prepared to update once v1.0 is published.
Comments and proposed revisions are welcomed by email to founders@skalor.xyz. Substantive changes between draft revisions are tracked in the changelog (§19).
This draft (v0.3, October 2026) supersedes v0.2 (August 2026), which superseded v0.1 (May 2026). Both remain published for the document record.
Source of truth. Where this document and the method document served at GET /bureau/method disagree, the method document is correct and this document has a bug. Please report it.
3Two Phases
A principal that lets an agent spend needs two things, in this order.
- Phase 1, fiduciary control (the core offering). The principal’s mandate is enforced before the payment, not reconciled after it. The gates in §5 run on every request, fail fast, and record why they decided what they decided.
- Phase 2, the bureau. Once every decision is recorded against a mandate, the record itself becomes evidence. A counterparty deciding whether to transact with an unknown agent can ask how that agent has behaved inside its own mandate. An agent deciding whether to pay an unknown vendor can ask whether that vendor honours what it signs.
Scoring therefore covers both sides of a payment: the agent that spends (§7) and the vendor it pays (§10). Both methods share one statistical style: deterministic, every point attributable to a factor, every factor attributable to counts a third party can check.
The bureau does not replace identity. Request-signing schemes answer what is connecting; credentials answer who stands behind it. A KYA rating answers the remaining question: how has it behaved with money at stake, inside the limits its principal set.
4Conformance & Terminology
The key words must, must not, should, should not, and may in this document are to be interpreted as described in [RFC 2119] and [RFC 8174].
- Agent
- Autonomous software that proposes payments on behalf of a principal. Identified by a uuid and, where it has one, a DID.
- Controller
- The organisation that registers an agent and sets its mandate. The controller is part of what the agent rating rates, because an agent identifier can be discarded and re-registered in one call.
- Mandate
- The controller’s policy for an agent: vendor allowlist, per-transaction limit, daily budget, human-approval threshold, expected runtime, and anomaly-enforcement mode.
- Decision
- One evaluated payment request: cleared, denied with a reason, or parked for a human.
- Clearance receipt
- An Ed25519-signed record of a cleared decision, verifiable offline (§6).
- Seller
- A vendor that takes payment, identified by its origin and the DID
did:web:<origin>. - Bureau Operator
- An entity that issues ratings under this specification. Skalor is the only operator today.
- Tier
- One of
A,B,C,D,UNRATED, or (agents only)REVOKED. An agent tier computed over a reduced denominator carries a trailing star, e.g.B*.
5Phase 1: Fiduciary Control
Every payment request an agent submits is evaluated by the gates below, in order, before any credential is signed. A refusal at any gate stops evaluation and is recorded with its reason.
| Gate | Check | Status |
|---|---|---|
| 0 · Runtime attestation | The agent’s model, prompt and tool configuration hash matches the runtime its mandate was written against. Advisory or strict per agent. | Live |
| 1 · Kill switch | The agent is active. A paused agent cannot spend. | Live |
| 1.5 · Sanctions | The vendor is screened against a sanctions list. | Live |
| 2 · Vendor allowlist | The vendor is one the mandate names. | Live |
| 3 · Per-transaction limit | The amount is within the mandate’s per-transaction ceiling. | Live |
| 4 · Daily budget | The amount fits the rolling 24-hour budget. | Live |
| 5 · Human approval | Above the mandate’s approval threshold the request is parked for a human (§9). | Live |
| 6 · Behavioural anomaly | The request is scored against the agent’s own history and tiered safe, watch, warn or critical. Enforcement per agent: off, shadow, soft or strict. | Live |
Every request carries an intent string, and every decision records the caller that submitted it. Only decisions the agent’s own controller submitted are evidence for the bureau (§7.1).
6Evidence: Receipts and Keys
6.1 Clearance receipts Live
A cleared decision carries an Ed25519 signature over the canonical JSON of its payload (keys sorted at every level, UTF-8, no whitespace). Wire format:
skalor_v1.<base64url(canonical_json_payload)>.<base64url(ed25519_sig)>
skalor_v2.<...> v2 adds the Gate 0 runtime attestation and its statusThe payload binds the decision to the agent, the organisation, the vendor, the amount, the gates evaluated, a policy_hash (SHA-256 of the mandate snapshot in force at decision time), and the key id kid. Receipts verify offline against the published key set, in the browser at skalor.xyz/verify, or with the verifiers in @skalor/sdk and skalor-sdk.
6.2 Rating attestations Live
Agent and seller ratings are signed as compact JWTs (alg: EdDSA, typ: jwt), issued by did:web:skalor.xyz, typed by a vct (https://skalor.xyz/vct/agent-rating/v1 or https://skalor.xyz/vct/seller-rating/v1), and valid for 24 hours. The signed claim set is the public response body and nothing more.
6.3 The published key set, as it stands
Two verification documents are published today. This section describes them as they are.
| Document | Contents | Signs |
|---|---|---|
skalor.xyz/.well-known/skalor-key.json | A JWKS holding one Ed25519 key, kid: skalor-attest-2026-04, generated 19 April 2026. | Clearance receipts (skalor_v1, skalor_v2), the older bureau score receipt (skalor_bureau_v1), and the revocation list (skalor_crl_v1). |
/bureau/.well-known/did.json and /seller-registry/.well-known/did.json | The DID document for did:web:skalor.xyz, holding the Ed25519 key that signs rating JWTs. | Agent and seller rating attestations. |
The JWKS format allows several keys selected by kid, and verifiers must select by kid rather than assume a single key. No rotation has been performed: the key above has signed every receipt since April 2026, and no rotation schedule is in force. Rotation is an open problem (§16).
7Agent Rating: skalor-agent-rating/v1.3 Live
The agent rating is served at GET /bureau/agents/:id for every agent its controller has opted into the public bureau. It produces a score, a denominator, a tier, the factors behind them, and the counts behind the factors.
7.1 Subject and evidence eligibility
The subject is an agent together with its controller. A decision is evidence only if the agent’s own controller submitted it: the request carried an active API key owned by the agent’s organisation (and bound to that agent, if bound at all). A decision recorded as unauthenticated is read by nothing in the method, and the number excluded is published as evidence.unauthenticated_decisions_excluded. A request presenting a key that does not own the agent is refused and writes no decision. Decisions written before the rails recorded the caller are read as before.
The rating is computed over the agent’s most recent 5,000 eligible decisions. When that window truncates the history, the response says so.
7.2 Materiality
Each decision carries an evidence weight measured against the agent’s own per-transaction limit:
w = min(1, amount_usd / (0.10 × per_transaction_limit))
(agents with no limit set use a $1.00 floor in place of 0.10 × limit)A decision at or above a tenth of the agent’s ceiling is one full observation; a dust payment is a fraction of one. These weights, not row counts, are the n in every rate below. An agent is UNRATED until the weights sum to at least 1.
7.3 Laplace smoothing and the confidence blend
Every rate-based factor uses the same estimator, identical to the seller method’s:
n = good + bad (materiality-weighted)
smoothed = (good + 1) / (n + 2) Laplace
confidence = min(1, n / N_full)
value = 0.5 + (smoothed − 0.5) × confidenceThin evidence is pulled toward the 0.5 prior rather than toward an extreme. A factor’s contribution is round(value × weight). The response also carries a confidence band over the agent’s total material weight: none (0), low (below 10), moderate (10 or more), high (50 or more). It describes how much has been observed, not how the agent behaved.
7.4 Factors
| Factor | Weight | Rule |
|---|---|---|
mandate_compliance | 300 | Cleared decisions against denials for a mandate breach (allowlist, per-transaction limit, daily budget, sanctions, Gate 6 block, Gate 0 mismatch, kill switch). N_full = 50. Treasury and settlement failures are excluded. Pending decisions count for neither side. Since v1.3 a parked decision a human approved counts on the compliant side (§9). |
attestation_coverage | 200 | Of cleared decisions where a clearance receipt was available, those carrying both a matching Gate 0 runtime attestation and a signed receipt, against the rest. N_full = 30. Unscorable only when no cleared decision in the agent’s whole history could have carried a receipt (§7.7). |
anomaly_history | 150 | Gate 6 tiers: safe counts clean; watch ×0.5, warn ×1, critical ×2 count against. N_full = 50. Decisions where Gate 6 did not run count for neither side. |
operator_review | 100 | Human-reviewed decisions: Gate 6 flags labelled false positive or benign, and (since v1.3) Gate 5 parked decisions a human approved, count as vindicated; flags confirmed true positive count against. A Gate 5 denial counts for neither side. N_full = 10. |
settlement_reliability | 100 | Settlements confirmed against failed. N_full = 20. Pending counts for neither side. |
controller_standing | 100 | A ladder over the controller’s record across every agent it stands behind (§7.5). |
tenure | 50 | min(1, days since first decision / 90) × min(1, material weight / 100). Time alone earns nothing. |
The weights sum to 1000.
7.5 Controller standing and slashes (v1.1)
ladder = 1
− min(1.0, 0.5 × executed slashes) two executed slashes exhaust it
− min(0.5, 0.25 × pending or disputed slashes)
− 0.3 if any active revocation for fraud, compromise or policy violation
ladder = clamp(ladder, 0, 1)
standing = ladder ≥ 0.5 ? 0.5 + (ladder − 0.5) × min(1, controller material weight / 200)
: ladderSlashes and adverse revocations are counted across every agent the controller stands behind, and this is the only place the controller’s slash record is charged: once, on every agent under that controller, bounded by this factor’s 100 points. Good standing must be earned by record; bad standing is never discounted for thin evidence, so a fresh organisation cannot launder a slashed history. Under v1 a sibling’s slashes were also charged in full as a per-agent penalty on every agent of the controller; v1.1 removed that double charge.
7.6 Penalties
Penalties carry weight 0: they subtract from the score without enlarging what it is measured against.
| Penalty | Rule | Shape |
|---|---|---|
mandate_breach_incidence | Tariff of −40 per scope breach (unlisted vendor, paused agent, unexpected runtime) and −15 per budget breach (per-transaction or daily), scaled by breach rate. Floor −5 per breach, cap −240. | Rate-scaled since v1.2 |
confirmed_true_positives | −60 per decision a human confirmed as a true positive, cap −240. | Flat |
ofac_matches | −150 per decision blocked at Gate 1.5 for a sanctions match, cap −300. | Flat |
slash_events | −150 per executed slash (cap −300) and −75 per pending or disputed slash (cap −225) recorded against this agent’s own deposit. | Flat |
The breach formula (v1.2):
tariff = 40 × scope_breaches + 15 × budget_breaches
rate = breachW / (compliantW + breachW) the two sums mandate_compliance scores
(1 when both are zero)
scaled = round(tariff × min(1, rate / 0.10))
penalty = min(240, max(5 × (scope_breaches + budget_breaches), scaled))A breach rate of 10% or more pays the full v1.1 tariff, so the change only ever relieves. Sanctions denials count toward the rate (they are mandate breaches) but are charged only on their own penalty. Worked example: 3 scope and 2 budget breaches against 95 material clearances and 5 material breaches gives a tariff of 150, a rate of 5%, a multiplier of 0.5, a scaled charge of 75 and a floor of 25, so the penalty is −75.
Sanctions hits, confirmed true positives and slashes stay flat per event. Each records a judgement a human or a list stood behind, and none is drawn from the population of the agent’s decisions, so there is no honest denominator to divide by. Scaling a sanctions penalty by volume would make a sanctions hit cheaper the more an agent transacts.
7.7 Denominator, scorability and the starred tier
denominator = Σ factors[].weight 1000 less any unscorable weight
score = clamp(Σ factors[].contribution, 0, denominator)
uncapped_score = round(1000 × score / denominator)
normalized_score = min(uncapped_score, the binding ceiling, if any)A factor is unscorable only when the evidence it scores cannot exist on the agent’s route. Today exactly one factor can be: attestation_coverage, when every one of the agent’s cleared decisions, counted over its full history rather than the window, was decided where no clearance receipt could be issued. Its 200 points then leave the denominator and the reason is published in unscorable_factors. One cleared decision where a receipt was available makes it scorable permanently, so no agent can widen the exemption by producing less evidence. The two full-history counts behind the predicate are published as evidence.cleared_decisions_all_time and evidence.attestable_decisions_all_time.
A factor scored over no evidence at all sits at the 0.5 prior, keeps its weight, and is listed in prior_held_factors. On the main clearance route settlement_reliability is permanently in this state, because that route records every clearance as pending settlement and never learns otherwise.
A tier is only comparable with another tier over the same denominator. When the denominator is below 1000 the tier carries a trailing star (B*), tier_letter carries the bare grade, and comparability.comparable_with_full_denominator_ratings is false. UNRATED and REVOKED are never starred.
7.8 Ceilings by controller verification
| Controller rung | Ceiling on normalized_score | Proof |
|---|---|---|
none | 700 (tier A out of reach) | Known only as a row id. |
domain | 850 | Published a Skalor-issued challenge at https://<domain>/.well-known/skalor-controller.json. |
kyb | 1000 (no ceiling) | A named operator checked the controller’s legal identity. |
Every applicable ceiling is published with binding set to whether it held the score down, and the uncapped figure beside it. A ceiling acts on normalized_score only and never restates score, so the contributions always add up to the published score.
7.9 Tiers
| Tier | Rule |
|---|---|
A | normalized_score ≥ 800 |
B | ≥ 600 |
C | ≥ 400 |
D | < 400 |
UNRATED | Less than one full material observation. Not a low score: no score. |
REVOKED | An active, unrescinded revocation stands against the agent. |
7.10 Illustrative response
The shape below is illustrative; the numbers are internally consistent but describe no particular agent.
{
"method": "skalor-agent-rating/v1.3",
"data_provenance": "live", // or synthetic_fixture | test_harness
"rating": {
"score": 748, // exactly Σ factors[].contribution
"denominator": 800, // attestation_coverage unscorable
"uncapped_score": 935, // round(1000 × 748 / 800)
"normalized_score": 700, // held at the 'none' ceiling
"tier": "B*",
"tier_letter": "B"
},
"comparability": { "comparable_with_full_denominator_ratings": false, ... },
"confidence": { "band": "high", ... },
"factors": [ { "key": "mandate_compliance", "weight": 300, "contribution": 299, "detail": "..." }, ... ],
"unscorable_factors": [ { "key": "attestation_coverage", "weight": 200, "code": "no_clearance_receipt_issuer_on_route", ... } ],
"prior_held_factors": [ { "key": "settlement_reliability", "weight": 100, "contribution": 50, ... } ],
"ceilings": [ { "code": "unverified_controller", "value": 700, "binding": true, ... } ],
"evidence": { "cleared_decisions_all_time": 200, "unauthenticated_decisions_excluded": 0, ... },
"recompute": { "method_url": "/bureau/method", "source": "...", "arithmetic": "..." }
}Ratings of seeded registry fixtures and smoke-test agents are answered, not hidden, with data_provenance set accordingly and the notice first in the summary.
8Controller Verification
8.1 Earning a rung Live
The domain rung is self-serve through POST /controller-verify, authenticated with the organisation’s own API key. Skalor issues a 256-bit random challenge (stored only as a SHA-256 hash); the controller publishes it at /.well-known/skalor-controller.json on its domain; Skalor fetches it back over HTTPS, refusing redirects and private addresses, with a 5-second timeout and a 16 KB cap. A challenge is live for 7 days and allows 10 attempts. The kyb rung has no endpoint: an operator sets it by hand and the record names who checked and when.
8.2 Validity, grace and lapse Live
A rung is a present-tense claim, so its proof ages out. Expiry is derived from organizations.verified_at, not stored:
expires_at = verified_at + 90 days (domain) | 365 days (kyb)
grace_ends_at = expires_at + 14 days
now < expires_at current the rung counts
now < grace_ends_at grace the rung counts; every rating says when it lapses and how to renew
otherwise lapsed the ceiling is computed as 'none' (700)On lapse the ceiling’s code becomes lapsed_controller_verification and its reason states when the proof was taken, when it expired and when it fell. A rung with no verification date is treated as lapsed. Every rating publishes a verification block carrying verified_at, expires_at, grace_ends_at and state.
8.3 Re-check Live
A domain rung is renewed by POST /controller-verify {"action":"recheck"}, which re-fetches the same published document and compares it against the hash kept from the original challenge. Success moves verified_at to now. Failure moves nothing: it neither extends nor ends the rung, which lapses on its own schedule. Ten failed re-checks require a fresh challenge. A kyb rung is renewed by an operator repeating the check.
9Gate 5: Human Approval
9.1 Parking Live
A request above the mandate’s approval threshold that has passed Gates 0 through 4 is refused and recorded for a human with denial_reason = "REQUIRES_HUMAN_APPROVAL". The response tells the agent where to poll: GET /gate5-resolve?tx_id=….
9.2 Resolution Live
POST /gate5-resolve records approved or denied (a denial requires a note). The caller must present an API key of the organisation that owns the agent, and that key must not be bound to the agent itself, nor be the key that submitted the parked request: an agent cannot resolve its own escalation. A resolution is final. It is written onto the parked decision with the resolving key’s row id, and only a resolution carrying that id is read by the rating.
In the rating (since v1.3), an approval counts on the compliant side of mandate_compliance and the vindicated side of operator_review, at its materiality. A denial, or no resolution, counts for neither side: a no from the principal is not a breach by the agent. The counts are published as evidence.parked_decisions_approved and parked_decisions_denied, so a reader can see how much of a rating rests on the controller’s own review.
9.3 The one-time release grant Live
Resolving does not itself move money. An approval writes a grant onto the parked decision, bound to:
- the parked decision itself and the agent that was parked;
- the counterparty, as the vendor host the parked request named;
- the currency and rail the parked request would have settled on;
- a ceiling equal to the parked amount (a resubmission may be less, never more);
- an expiry 24 hours after the approval.
The agent spends it by resubmitting with approval_ref = <parked tx id>, authenticated with its own key. The grant bypasses Gate 5 only. Gates 0 through 4 run before it and Gate 6 runs after it, unchanged. The grant is claimed only once every gate has passed, by a conditional update that succeeds for exactly one request; it is released again if clearance fails afterwards for an infrastructure reason before a credential leaves. The key that approved cannot spend the grant. Grants are issued only on rails whose clearance path reads them; elsewhere an approval counts in the rating but releases nothing, and the response says so.
9.4 Binding approval to a person Proposed
Today an approval is bound to an API key of the controller’s organisation, not to a signed-in person. The method cannot distinguish a considered review from a rubber stamp, and says so. What bounds the trust: the agent’s own key cannot resolve, and operator_review is smoothed against ten full reviews, so it can rise at most 50 points above the prior. A future revision should bind each resolution to an authenticated individual and publish that binding, without disclosing who.
10Seller Rating: skalor-seller-rating/v1 Live
The seller registry rates vendors from signed offers and receipts they issue. Artifacts are JWS, signed EdDSA or ES256.
10.1 Identity: did:web
A seller is identified by its origin and the DID did:web:<origin>. To verify an artifact the registry resolves the signer’s did.json over HTTPS (redirects refused by default, no private addresses, 64 KiB cap, 5-second timeout), selects the key by RFC 7638 thumbprint, and requires the signer’s did:web host to equal the host of the resource the artifact is for. Artifacts are deduplicated by hash, so a replayed receipt counts once.
10.2 Factors
| Factor | Weight | Rule |
|---|---|---|
identity | 200 | 1 if the latest did.json resolution succeeded and no artifact was signed for a resource the signer is not authorised for; 0.5 if some were; 0 if resolution failed. |
honour | 300 | Receipts matching a prior signed offer within its validity, against expired-at-issue receipts, receipt-missing reports and disputes lost. Laplace-smoothed, N_full = 20. |
receipts | 150 | Verified receipts against receipt-missing reports. Laplace-smoothed, N_full = 20. |
integrity | 150 | 1 − min(1, 5 × share of artifacts failing signature or authorisation). |
tenure | 100 | min(1, days observed / 90) × min(1, verified artifacts / 100). |
activity | 100 | min(1, log10(1 + verified artifacts in 30 days) / 3). |
disputes (penalty) | 0 | −50 per open dispute, cap −200. Open = opened − resolved for seller − resolved for buyer. |
Score = clamp(Σ contributions, 0, 1000). Tiers use the same bands as agents (A ≥ 800, B ≥ 600, C ≥ 400, D < 400). UNRATED means no verified offers or receipts. The seller method has no denominator reduction and no ceilings. Ratings are public; raw observations are not, because they carry payer addresses.
11Recomputability & Endpoints
11.1 How a third party recomputes a score Live
- Fetch the rating and the method document. The method version in the rating names the rule applied.
- Check the arithmetic: the contributions sum to
score, the weights sum todenominator, anduncapped_scoreandnormalized_scorefollow from §7.7 and the published ceilings. - Recompute each factor from the counts its
detailstring and theevidenceblock publish, using §7.3 to §7.6 (or §10.2 for sellers). Materiality-weighted sums are published to three decimals, so a recomputed contribution can differ from the published one by a point of rounding. - Recompute the scorability predicate from the two full-history counts.
- Verify the attestation JWT against the published DID document, and check the revocation list.
The method is deterministic: the same rows under the same method version give the same score. The response’s recompute block names the source columns and states every equation.
Limit. The bureau publishes aggregates, never individual decisions, counterparties or amounts. A third party can therefore recompute every score from its published counts, but cannot independently re-derive those counts from the underlying rows. A counterparty holding its own clearance receipts can check that its decisions exist; it cannot see anyone else’s. See §16.
11.2 Endpoints
Bureau paths are relative to Skalor’s public functions base. All reads below are public and need no credential.
| Endpoint | Returns | Status |
|---|---|---|
GET /bureau/agents/:id | Agent rating; :id is the uuid or the DID. Opted-in agents only; anything else is the same 404. | Live |
GET /bureau/agents/:id/attestation | The same body plus a signed JWT. | Live |
GET /bureau/method | The published agent method. | Live |
GET /bureau/verify?jwt= | Verifies an agent-rating attestation. | Live |
GET /bureau/.well-known/did.json | The key that signs rating JWTs. | Live |
GET /bureau-list | Registry browse of opted-in agents (also at skalor.xyz/registry). | Live |
GET /bureau-crl | Signed revocation list. | Live |
GET /seller-registry/sellers/:origin | Seller rating with factors and evidence; /attestation adds the JWT. | Live |
GET /seller-registry/method | The published seller method. | Live |
skalor.xyz/.well-known/skalor-key.json | JWKS for receipts and the revocation list. | Live |
GET /bureau?agent_id= still serves the older Phase A.1 bureau score, unchanged for existing callers. It predates the method in §7 and is not the rating this RFC specifies.
12Revocation Live
A controller can revoke an agent’s bureau publication. While a revocation stands, the agent’s tier is REVOKED and it is not scored. Revocations recorded as fraud, compromise or policy violation also debit controller_standing on every agent of that controller (§7.5). GET /bureau-crl publishes the list, signed with the JWKS key in the skalor_crl_v1 wire format with a 300-second TTL, and omits organisation identifiers. Verifiers should consult it before relying on any attestation.
13Deposits and Slashing
A deposit is money the controller posts against an agent’s conduct, forfeited in part when a defined trigger fires. Nothing more.
Slash events Live. Recorded slashes (from critical anomaly findings, sanctions matches, or an operator confirming a true positive) count in the rating exactly twice: as the slash_events penalty on the agent they were recorded against, and once in controller_standing for every agent of the same controller.
Deposit amounts Proposed. Deposit records exist in the schema in demonstration mode only. The amounts have no money behind them, so the rating deliberately ignores them, and no deposit appears in any rating response. A production deposit, the published slashing policy it would be subject to, and how forfeited amounts reach a harmed counterparty are not built.
14Federation Proposed
Skalor is the only Bureau Operator. The bureau already serves ratings any party can verify offline, and a cross-organisation lookup by agent identity is used at onboarding (POST /bureau-lookup) Live. Federation between independent operators is not built. A future revision is expected to specify: how one operator pulls and verifies another’s attestations; that a composing operator must not re-sign another operator’s claim as its own; an operator directory; and a conformance check that an operator’s attestations verify against its published keys.
15Security Considerations
15.1 Replay
Rating attestations expire 24 hours after issue. Verifiers should reject expired attestations and check the revocation list.
15.2 Score gaming
Materiality defeats padding with dust. The confidence blend keeps thin records near the prior. Tenure requires activity. Decisions not submitted by the controller are excluded, so a stranger who learns an agent’s uuid cannot write denials into its record. The scorability predicate is evaluated over full history, so an agent cannot evict its own evidence to open an exemption.
15.3 Privacy
Ratings publish aggregates only. No counterparty, amount, per-decision timestamp or intent text is published, and of the controller only its id. Agents not opted in (bureau_public = false) return the same 404 as agents that do not exist. Ratings refer to software and the organisations behind it and must not be used to make decisions about natural persons.
15.4 Self-review
Human review (Gate 6 labels and Gate 5 resolutions) is the controller’s word. Its effect is bounded and its counts are published (§9.4).
16Open Problems
- Mandate normalisation Proposed. The agent rating measures compliance with whatever mandate the controller set. A mandate loose enough that nothing breaches earns the same compliance as a tight one. Planned for v1.4: publish the mandate (limits, allowlist breadth, approval threshold) alongside the rating, and weight
mandate_complianceby the mandate’s strictness. - Session and streaming mandates. The gates and the rating reason about discrete payment requests. Payments authorised once for a session and then streamed in many small increments are not modelled: materiality would read each increment as dust, and no mandate field expresses a session budget.
- Sybil resistance on organisation creation. Creating an organisation is self-serve and free. The 700 ceiling, the confidence blend, and undiscounted bad controller standing limit what a fresh organisation can claim, but a new organisation with a new agent can still reach tier B. A cost or proof at creation is unspecified.
- Cross-rail identity. An agent is identified by its uuid and, where present, its DID. The same software operating under different identifiers on different rails, or with different operators, is not linked, and the onboarding lookup matches only exact identifiers.
- Signing-key rotation. One receipt key has been in use since April 2026 and has never been rotated. The JWKS supports several keys by
kid, but no rotation cadence, overlap period or compromise procedure is in force, and the revocation list is signed by the same key it would need to outlive. - Sparse evidence. Most agents have thin records. One full material observation is enough to leave
UNRATED; human review is rare by nature; and settlement outcomes are never observed on the main clearance route, so that factor sits at the prior for every agent there. The confidence band states this but does not solve it. - Row-level verifiability. Recomputation today starts from published counts (§11.1). A way to commit to the underlying decisions so that counts can be checked without disclosing them is unspecified.
17Governance
This document is editorial-controlled by Skalor Labs through v1.0. Substantive changes are published as numbered drafts until then. Compatibility-breaking changes after v1.0 must be published as a new RFC rather than as a revision to this one.
A rating method changes only by a new method version (for example skalor-agent-rating/v1.4), with the change stated in the method’s own changelog. Every rating names the version that produced it.
When independent operators exist, Skalor Labs intends to move stewardship of this specification into a neutral, member-governed structure, with Skalor as a founding member rather than owner.
18References
- [RFC 2119]Bradner, S. “Key words for use in RFCs to Indicate Requirement Levels.” BCP 14, RFC 2119, March 1997.
- [RFC 7515]Jones, M., Bradley, J. and N. Sakimura, “JSON Web Signature (JWS),” RFC 7515, May 2015.
- [RFC 7638]Jones, M. and N. Sakimura, “JSON Web Key (JWK) Thumbprint,” RFC 7638, September 2015.
- [RFC 8032]Josefsson, S. and I. Liusvaara, “Edwards-Curve Digital Signature Algorithm (EdDSA),” RFC 8032, January 2017.
- [RFC 8174]Leiba, B. “Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words.” BCP 14, RFC 8174, May 2017.
- [RFC 8615]Nottingham, M. “Well-Known Uniform Resource Identifiers (URIs),” RFC 8615, May 2019.
- [DID-CORE]W3C Decentralized Identifiers (DIDs) v1.0. W3C Recommendation.
- [DID-WEB]W3C Credentials Community Group, did:web Method Specification.
- [VC-DM]W3C Verifiable Credentials Data Model 2.0. W3C Recommendation.
19Changelog
v0.3 · October 2026.
- Rewritten against the running code. Every mechanism is labelled Live or Proposed.
- §3, §5 (new): framing as two phases. Fiduciary control (the gates) is the core offering; the bureau is phase 2, built on its evidence, and rates both agents and vendors.
- §7 (new): the agent rating
skalor-agent-rating/v1.3specified in full: factors and weights, materiality, Laplace smoothing and confidence, controller standing, penalties, denominator and scorability, the starred tier, ceilings, tiers. Replaces v0.2’s six-component record and itsAAAtoCCCtier scale. - §7.5: controller slashes charged once, inside controller standing (method v1.1).
- §7.6: breach penalty scaled by breach rate with a per-breach floor and cap, while sanctions hits, confirmed true positives and slashes stay flat (method v1.2).
- §9 (new): Gate 5 parking, human resolution counting in the rating (method v1.3), and the one-time release grant. Binding approval to a signed-in person listed as Proposed.
- §8 (new): controller verification rungs, validity windows, grace period, re-check and lapse.
- §10 (new): the seller rating
skalor-seller-rating/v1and did:web identity. - §6: the published key set described as it is (one receipt key, never rotated); the monthly rotation and 30-day overlap claims removed.
- §11 (new): recomputation procedure, its limit, and the endpoint list.
- §13: stake section reduced to what is built. Slash events count in the rating; deposits are demonstration-only and Proposed. Denominations, recovery waterfall and the normative stake block removed.
- §14: federation between independent operators and issuer bonds moved to Proposed.
- §16 (new): open problems, including mandate normalisation planned for method v1.4.
- Removed: contract-level enforcement reference, validator-node claim, proposed smart-contract tier anchor, third-party analogies and citations, and settlement wording tied to a specific network.
v0.2 · August 2026. See v0.2.
v0.1 · May 2026. Initial public draft. See v0.1.
The rating methods this RFC describes are served as machine-readable documents at GET /bureau/method and GET /seller-registry/method. Comments to founders@skalor.xyz.