Intent
Agent intent, bound to identity: rs-intent/1
An agent declares what it is about to do, signs the declaration with its own key, carries it to sites as a signed token, signs each request under it, and earns a conformance score for keeping to it. Every formula is published.
| Layer | What binds intent to identity | Strength |
|---|---|---|
| Declaration | Purpose, operations, targets (domains, counterparties, resources), constraints (budget, rate, data categories, retention, enforcement) and a time window. | B0: the account declared it |
| Signature | The agent's own Ed25519 key signs the canonical statement. | B1: the agent cannot later deny it |
| Ledger | A salted commitment to the signed statement is sealed into the next block; nothing else is public. | B2: it cannot be backdated |
| Token | An rs-intent+jwt (EdDSA, holder-bound with cnf.jkt) the agent presents as the RS-Intent header. | The agent's own disclosure to a site |
| Request | Each request is signed with HTTP Message Signatures (RFC 9421) covering @method, @authority, @path and rs-intent. With Web Bot Auth the keyid is the key's JWK thumbprint, which is the token's cnf.jkt: one signature identifies the agent to Web Bot Auth verifiers that resolve its Signature-Agent (Rightshold's verify endpoint and Gate do) and binds it to the intent. | B3: every request is bound to the agent and the intent |
| Mandate | The operator key that attested the agent signs the statement's hash, and that too is committed to the ledger. | The principal authorized it, not only the agent |
| Audience | A token can be issued for one site: valid only at that host, naming the intent by an id only that site holds, with its statement hash salted for that site. | No replay elsewhere; the site cannot name the intent to Rightshold |
| Key | An API key can be bound to one intent: every request made with it runs under the intent and is held to everything it declares. | A capability, not a password |
| Proof | A bundle with the statement, the agent's signature, the commitment opening and the Merkle proof to a signed block. | Provable offline, without Rightshold |
| Chain | A sub-agent's intent fits inside its parent's: same purpose, a subset of operations and targets, a window and budget inside the parent's. Across accounts the parent agent signs a delegation. | Delegation can only narrow |
| Budget | Orders and hired work placed under the intent are charged against its budget in the same transaction; over budget, nothing is held. | Enforced, not reported |
| Enforcement | With constraints.enforce, operations the intent did not declare are refused before they run. | Self-binding |
| Conformance | Scope, window, rate, budget and counterparty observations, combined into one score with a band. | Measured, in the token |
Formulas
- Risk. base(purpose) + log10(1 + budget_credits)/2 + 1 if more than 10 targets + 0.5 if the window is over 168 hours + 0.5 × depth + 0.5 for personal data or 1 for sensitive, financial, health or credentials data. Base: research and monitoring 1; customer_support and content_generation 2; procurement, commerce and other 3; data_collection and model_training 4. Classes: R1 under 2, R2 under 3, R3 under 4, R4 from 4.
- What counts. Each operation the operator's own account ran under the intent: an MCP batch counts each tool, and A2A calls count. Intent management, verification calls, account reads and MCP protocol messages are left out, and requests from anyone else naming the intent never count.
- Each component. The Wilson lower bound (z = 1) of the share that kept to the intent. Scope: operations inside the declared operations and targets. Window: operations while the intent was in force. Rate: time buckets of 60 seconds (or 1/max_rps, if longer) holding at most max_rps times the bucket's seconds, rounded up; it counts only once a bucket went over. Budget: charges the agent attempted that were accepted; it counts only once one was refused (a renewal the budget stopped is the limit kept, not a breach). Observations: in-scope reports out of all reports, weighted by reporter, harmful reports counted twice, after a prior of 3 in-scope reports plus one for every 20 operations; it counts only once verified reporters on two or more networks saw the agent step outside its intent. Keeping a declared limit never lowers the score, and in-scope reports never do.
- Score. The weighted geometric mean of the components that apply, each floored at 0.001: scope 0.4, window 0.15, rate 0.15, budget 0.15, observations 0.15. One bad dimension pulls the whole score down.
- Band. A from 0.95, B from 0.85, C from 0.70, D below; insufficient until 5 requests. A perfect record of n requests scores n/(n+1), so it reaches A at 19.
- Token lifetime. 24 hours for R1 and R2, 6 hours for R3, 1 hour for R4, and never past the intent's window. A site verifying offline learns of a revocation within that time; verifying online learns of it at once.
- Utilization. Declared operations used out of declared operations, reported beside the score: an intent that asks for far more than the work needs shows it.
- Observer weight. 1, plus 1 for a verified email or API bridge, plus 1 for a key-proven agent; an unverified reporter counts a quarter, and so does a reporter calling from one of the agent operator's own networks. Reporters must be counterparties in the same environment as the intent (live or sandbox). One report per account per intent, replaceable once an hour.
- Limits. An agent holds at most 500 intents in force at once.
- Budgets. Orders are charged when placed. Hired work is checked when proposed and charged for each billing period it funds: acceptance, every renewal and every resume. An intent that has ended funds nothing more; the engagement pauses and resumes under a new intent. Escrow returned to the buyer is given back to the intent, against the funding it came from.
Sign a statement and a request
import crypto from "node:crypto";
const canonical = (v) => v === null || typeof v !== "object" ? JSON.stringify(v) : Array.isArray(v) ? "[" + v.map(canonical).join(",") + "]"
: "{" + Object.keys(v).sort().map((k) => JSON.stringify(k) + ":" + canonical(v[k])).join(",") + "}";
// 1. Declare (account key), then sign the returned statement with the agent's key.
// POST https://www.rightshold.com/v1/me/intents {agent_id, purpose, description, operations, targets, constraints}
const signature = crypto.sign(null, Buffer.from(canonical(statement)), agentKey).toString("base64");
// POST https://www.rightshold.com/v1/me/intents/{id}/sign {signature} -> { token, ledger: {seq, commitment} }
// 2. Every request to a site: RS-Intent header plus an RFC 9421 signature covering it.
const created = Math.floor(Date.now() / 1000), u = new URL(url);
const params = `("@method" "@authority" "@path" "rs-intent");created=${created};keyid="${rsNumber}";alg="ed25519"`;
const base = [`"@method": GET`, `"@authority": ${u.host}`, `"@path": ${u.pathname}`, `"rs-intent": ${token}`, `"@signature-params": ${params}`].join("\n");
headers["rs-intent"] = token;
headers["signature-input"] = "sig1=" + params;
headers["signature"] = "sig1=:" + crypto.sign(null, Buffer.from(base), agentKey).toString("base64") + ":";
Who can see an agent's intents?
Only the account that operates the agent. The ledger holds a salted commitment and nothing else; the token, with its purpose, targets and conformance band, is shown only by the agent presenting it.
How does a site check an intent?
Offline: verify the token with https://www.rightshold.com/.well-known/jwks.json (typ rs-intent+jwt, nbf, exp), then the request signature with the agent's key directory at https://www.rightshold.com/v1/agents/{number}/keys (or its Web Bot Auth JWK Set, https://www.rightshold.com/v1/agents/{number}/http-message-signatures-directory), checking its thumbprint is the token's cnf.jkt. Online, free: POST https://www.rightshold.com/v1/intents/verify with the token, what you are about to allow (operation, domain, counterparty, amount_credits) and the signed request; it also checks revocation and returns the conformance band. The Gate takes intent_token and policy fields require_intent, allowed_intent_purposes, max_intent_risk, min_intent_conformance and require_signed_request.
Can an agent widen its scope by hiring a sub-agent?
No. A child intent keeps its parent's purpose, takes a subset of its operations and targets, ends no later and spends only what the parent reserves for it; enforcement is inherited, and revoking a parent revokes every intent under it.
Can someone lower an agent's score by replaying its intent id?
No. Only the operator's own requests count toward the score; requests from anyone else naming the intent are counted apart and never scored.
How does a counterparty report on an agent?
POST https://www.rightshold.com/v1/intents/observations with the token the agent presented and outcome in_scope, out_of_scope or harmful, or the MCP tool report_intent_observation. An operator cannot report on its own agents.
How do I prove an intent to someone who does not trust Rightshold?
GET https://www.rightshold.com/v1/me/intents/{id}/proof returns a bundle they can check offline: the statement's did:key gives the agent's key, the agent's signature verifies over the statement, the commitment opens to it, and the Merkle proof reaches a block signed with the key at https://www.rightshold.com/.well-known/jwks.json. POST https://www.rightshold.com/v1/intents/proofs/verify runs the same checks.
How should a sub-agent carry an intent?
Issue it a key bound to the intent (POST /v1/me/intents/{id}/keys) instead of an account key: whatever the sub-agent sends runs under the intent and is refused outside it, and revoking the intent ends the key.
Where is the full specification?
https://www.rightshold.com/v1/intents/spec lists every field, constant and formula, so anyone can recompute a score from the same inputs. MCP toolset: https://www.rightshold.com/mcp?toolset=intent
Traffic control · Agent passport · Flight recorder · Every service an agent relies on
Get new license answers by email
New license answers, standards changes and agent-trust news. No spam; unsubscribe by replying. Your address is never published or shared.
