Enruta

Docs · Concepts

Identity anchors

LiveUpdated

Agents and Platforms. Written for people who build agents, the hosts they run in, and the people and companies the agents pay for and PSPs, acquirers, orchestrators, dispute services and agent platforms that act for many customers.

An anchor is one verifiable fact about an agent. Enruta verifies it, keeps the evidence and its hash, and seals a kya_attestation record for it. From then on every decision and every payment record carries the agent’s anchors and the assurance level they add up to.

The six anchors

`type`What you sendVerified when
agent_key{ jwk, proof }: proof is a compact JWS (ES256 or EdDSA) by that key over { agent_id, nonce, iat }, with a nonce from POST /v0/attestations/nonceThe signature verifies with the key; the key is then bound to the agent
code_digest{ digest: "sha256:…", source? }A workload identity token in the same call binds the digest (GitHub’s job_workflow_sha or sha, or a digest claim); on its own it stays unverified
workload_identityAn OIDC token from GitHub Actions (https://token.actions.githubusercontent.com), Vercel (https://oidc.vercel.com/<team>) or Google (https://accounts.google.com), audience https://api.enruta.aiIt verifies against the issuer’s published keys, the audience matches and it has not expired; Enruta keeps iss, sub, repository, ref, sha, workflow, project and environment
hardware_attestationAn AWS Nitro Enclaves attestation documentThe certificate chain reaches the AWS Nitro root, the ES384 signature verifies and the nonce matches; Enruta keeps the PCRs and the module id. Other formats are kept as unverified, with the reason
legal_entityparty: user, platform or developeruser: the rail’s verification, today a Stripe Issuing cardholder that is active with nothing past due, on an account with nothing currently due (issuer: stripe). platform or developer: that organization’s own verification (issuer is its method)
collateral{ chain: "base" | "base-sepolia", token, holder, beneficiary?, min_amount }The token balance of the escrow or bond contract holder covers min_amount, read on chain. Stale after ten minutes, and read again when an x402 or MPP payment is decided
A legal_entity anchor for the user needs your own Stripe Issuing account connected as a rail. Enruta never collects identity documents: it reads a verification that the rail or the organization already has.

Assurance

LevelMeans
A0Nothing verified
A1A verified agent_key
A2A1, plus a verified workload_identity or hardware_attestation
A3A1 or A2, plus a verified legal_entity of any party; or, on x402 or MPP, A1 or A2 plus verified collateral that covers the amount

Every payment record sealed as enruta/0.3 carries it as agent.identity { assurance, anchors[] }, with A0 and no anchors when nothing is verified. Each anchor is summarized with its attestation_id, type, party, issuer, status (verified, stale, unverified or revoked), verified_at, expires_at and evidence_hash. The bundle keeps each anchor’s raw evidence in artifacts.identity[], and enruta-verify checks that sha256(JCS(evidence)) equals evidence_hash.

Bind a key

POST /v0/attestations/nonce

POST /v0/attestations
{
  "agent_id": "agt_procurement_02",
  "anchor": {
    "type": "agent_key",
    "jwk": { "kty": "OKP", "crv": "Ed25519", "x": "…" },
    "proof": "eyJhbGciOiJFZERTQSJ9.eyJhZ2VudF9pZCI6ImFndF9wcm9jdXJlbWVudF8wMiIsIm5vbmNlIjoi…"
  }
}

GET /v0/agents/agt_procurement_02/identity   → { "assurance": "A1", "anchors": [ … ] }

enruta attest key --agent <agt_id> --generate creates a key, proves it and binds it from the terminal; --jwk <file> uses a key you already hold. The v1 body, with registry, registry_ref and method, is still accepted for registry claims (Consent record and KYA attestation).

Attest from CI

Inside a GitHub Actions job, enruta attest workload requests the job’s OIDC token for the audience https://api.enruta.ai and submits it as a workload_identity anchor. The job needs permission to request the token:

permissions:
  id-token: write
  contents: read

steps:
  - uses: actions/checkout@v4
  - run: npx @enruta/cli attest workload --agent agt_procurement_02
    env:
      ENRUTA_API_KEY: ${{ secrets.ENRUTA_DEVELOPER_KEY }}

The CLI reads the Enruta key from ENRUTA_API_KEY; --digest sha256:<hex> adds a code digest for the token to bind. From another CI, pass its OIDC token in ENRUTA_WORKLOAD_TOKEN; Vercel and Google Cloud workloads can also submit their token to POST /v0/attestations directly.

  • The user. When the agent pays with a Stripe Issuing card, a legal_entity anchor with party: user reads the cardholder and the account’s requirements from Stripe.
  • A platform or a developer. The organization verifies itself once with POST /v0/org/verification: a company publishes the DNS TXT record enruta-verification=<token> on its domain; an individual goes through a Stripe Identity session, which runs in Stripe’s test mode today. The anchor then reads that verification.

Policy: identity requirements

"identity_requirements": [
  { "rails": ["x402"], "require": ["agent_key", "collateral"], "on_fail": "deny" },
  { "above": "1000.00", "require": ["agent_key", "legal_entity:user"], "on_fail": "deny" },
  { "above": "100.00", "require": ["agent_key", "workload_identity"], "on_fail": "step_up" }
],
"payee_identity": { "require_verified_above": "50.00", "on_fail": "step_up" }

The first row that matches the payment’s amount and rail applies, so list the strictest rows first. A missing anchor fails the agent_identity check with identity_insufficient, or with collateral_insufficient when the collateral does not cover the amount; on_fail decides whether the request steps up or is denied. payee_identity does the same for the merchant check, with payee_unverified; a checkout whose merchant signature fails is always denied, with payee_identity_mismatch.

Interfaces

SurfaceCall
RESTPOST /v0/attestations/nonce; POST /v0/attestations with { agent_id, anchor }; GET /v0/attestations; GET /v0/agents/{id}/identity; POST /v0/org/verification
CLIenruta attest workload, enruta attest key
SDKattestations.nonce, attestations.create, agents.identity, org.verification
Recordsagent.identity on payment records; one kya_attestation linked record per anchor
ConsoleThe agent page: assurance level, anchors, and how to attest