# Provider-Backed Agent Identity

Valiron now has a provider-independent verification foundation for identities
resolved through ANS, DNS, DIDs, API keys, and future standards. An ANS REST
resolver adapter is implemented and is registered at service startup when its
credentials and trusted CA bundle are configured. SDK 1.3 adds signing helpers,
a typed verification method, and dedicated identity gates for Express, Fastify,
Next.js, and framework-independent runtimes.

Verification calls require your operator API key and count toward your account’s monthly request allowance. Free includes 100,000 metered requests per UTC calendar month; Pro has no monthly request cap.

## Responsibility boundary

An identity provider establishes the agent's canonical identifier, verification
key or credential, endpoints, metadata, and authoritative organization or domain
binding. Valiron verifies the signed request proof, prevents replay, attaches
reputation and activity to the stable provider identity, and applies the API
seller's policy.

Provider trust evidence is not silently converted into Valiron reputation. ANS
may expose certificate integrity, lifecycle, trust-index, permission, or
historical signals; Valiron keeps those external signals separate from behavior
Valiron observed and from the endpoint owner's action-time policy.

Valiron does not register domains or issue provider identities.

## Identity statuses

| Status | Meaning |
| --- | --- |
| `anonymous` | No agent identity was supplied. |
| `claimed` | An identity was supplied, but resolution, authority binding, freshness, signature, or replay validation failed. |
| `verified` | The provider authority binding and signed request proof both passed. |

`verified` is pseudonymous. It proves that the caller controls the credential
bound to the agent identity; it does not identify an employee, customer, or
other human. This endpoint therefore always returns `humanIdentified: false`.

## Install

```bash
npm install @valiron/sdk@^1.3.0
```

ANS credentials and CA certificates are configured by Valiron on the hosted
verification service. API owners using the SDK do not need GoDaddy credentials
and must not expose Valiron's provider credentials to agents or browsers.

## Agent: sign and send a request

The agent signs the canonical Valiron payload using the private key bound to its
provider identity. The signing callback lets the agent use its existing key
manager, HSM, wallet, or provider-specific crypto library.

```typescript
import {
  agentIdentityProofHeaders,
  createAgentIdentityProof,
} from "@valiron/sdk";

const body = JSON.stringify({ operation: "quote", amount: 25 });
const proof = await createAgentIdentityProof({
  provider: "ans",
  agentId: "ans://v1.0.0.sales-agent.acme.com",
  body,
  sign: (payload) => agentKey.sign(payload),
});

await fetch("https://api.example.com/quote", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    ...agentIdentityProofHeaders(proof),
  },
  body,
});
```

The helper sends these proof headers:

| Header | Purpose |
| --- | --- |
| `x-valiron-identity-provider` | Resolver name, such as `ans` |
| `x-valiron-identity-agent-id` | Provider-canonical agent ID |
| `x-valiron-identity-signature` | Signature over the canonical payload |
| `x-valiron-identity-timestamp` | ISO 8601 signing time |
| `x-valiron-identity-nonce` | Unique replay-prevention nonce |
| `x-valiron-identity-key-id` | Optional provider key selector |

These headers are untrusted proof inputs until a Valiron identity gate validates
them. Never authorize a request merely because an `x-valiron-identity-*` header
exists, and never treat output metadata such as `x-valiron-tier` or
`x-valiron-route` as caller authentication.

There is intentionally no request-digest header. The receiving API computes
the SHA-256 digest from the exact request bytes so an agent cannot substitute a
digest for different content.

## API owner: enforce an identity policy

For Express, capture the raw bytes while parsing and install the provider
identity gate on the protected route:

```typescript
import express from "express";
import { ValironSDK, createValironIdentityGate } from "@valiron/sdk";

const app = express();
app.use(express.json({
  verify(req: any, _res, bytes) {
    req.rawBody = Buffer.from(bytes);
  },
}));

const valiron = new ValironSDK({ apiKey: process.env.VALIRON_API_KEY });

app.post("/quote", createValironIdentityGate({
  sdk: valiron,
  policy: {
    anonymous: "challenge",
    claimed: "block",
    verified: "allow",
    minReputationScore: 65,
    belowMinimumScore: "limit",
    highRisk: "block",
  },
}), handler);
```

Allowed requests expose the normalized result as `req.valironIdentity`.
`challenge`, `block`, and `limit` decisions return HTTP 401, 403, and 429
respectively. Verification errors fail closed with HTTP 503. Body-bearing
Express and Fastify requests return HTTP 400 if exact raw bytes were not
captured. Next.js middleware safely hashes `request.clone()` automatically.

Equivalent exports are `valironIdentityFastifyPlugin`,
`createValironIdentityNextMiddleware`, and `valironIdentityGateCheck`.

## Low-level verification request

`ValironSDK.verifyAgentIdentity(proof, policy)` calls
`POST /operator/identity/verify`. Advanced integrations can call it directly:

```json
{
  "provider": "ans",
  "agentId": "ans://v1.0.0.sales-agent.acme.com",
  "signature": "BASE64URL_OR_0xHEX_SIGNATURE",
  "timestamp": "2026-09-04T12:00:00.000Z",
  "nonce": "nonce-0123456789abcdef",
  "requestDigest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "keyId": "optional-provider-key-id",
  "policy": {
    "anonymous": "challenge",
    "claimed": "block",
    "verified": "allow",
    "minReputationScore": 65,
    "belowMinimumScore": "limit",
    "mediumRisk": "limit",
    "highRisk": "block"
  }
}
```

The signature covers the UTF-8 bytes of this exact payload:

```text
valiron-agent-request-v1
agent-id:<provider-canonical-agent-id>
timestamp:<timestamp-as-submitted>
nonce:<nonce-as-submitted>
request-digest:<lowercase-request-digest>
```

The request digest must use `sha256:` followed by 64 hexadecimal characters.
The nonce must contain 16–256 URL-safe characters. The default timestamp window
is five minutes, and nonce claims are retained for ten minutes.

The caller must calculate `requestDigest` from the protected request bytes and
apply the returned decision to that same request. This standalone verification
endpoint cannot infer the bytes of a separate downstream request.

## Response

```json
{
  "agentId": "ans://v1.0.0.sales-agent.acme.com",
  "provider": "ans",
  "organization": "Acme Corp",
  "identityStatus": "verified",
  "humanIdentified": false,
  "stableIdentityId": "identity:ans:<sha256>",
  "credentialId": "7b93c61c-e261-488c-89a3-f948119be0a0",
  "credentialVersion": "1.0.0",
  "verifiedAt": "2026-09-04T12:00:00.000Z",
  "reputationScore": 82,
  "riskLevel": "low",
  "policyDecision": {
    "action": "allow",
    "reason": "identity_verified"
  }
}
```

The action is one of `allow`, `limit`, `challenge`, or `block`. A verified
identity without stored reputation returns `reputationScore: null` and
`riskLevel: "unknown"`; it is not treated as having a negative score.

Failed proofs return HTTP 200 with `identityStatus: "claimed"` and a
machine-readable `failureReason`. See [Errors](./ERRORS.md#provider-backed-identity-errors).

## Replay and storage behavior

Each request nonce can be accepted only once. Verification returns HTTP 503 when the
replay store is unavailable, so the flow cannot fail open. Only verified
requests update the stable identity activity record. Failed claims cannot write
activity or reputation against the identity they attempted to impersonate.

For ANS, the stable reputation subject is the normalized `agentHost`, such as
`sales-agent.acme.com`. The versioned ANS name and Registry Authority UUID are
returned as credential identity fields. A new ANS version therefore gets a new
credential while retaining the same Valiron reputation subject.

## ANS resolver

As checked on 2026-09-10, the public ANS reference implementation uses
versioned `ans://v<major>.<minor>.<patch>.<agentHost>` names. Each version is an
independent registration with its own UUID and certificates, while `agentHost`
is the cross-version correlation key.

For an ANS claim, the adapter:

1. resolves the exact host and version through `POST /v1/agents/resolution`;
2. follows only a same-origin Registry Authority agent-details link;
3. requires the UUID, host, ANS name, version, and `ACTIVE` lifecycle state to match;
4. loads an identity certificate and validates its time window and signature chain against the configured CA roots;
5. requires an exact URI SAN for the versioned ANS name;
6. loads the current Transparency Log record, requires `ACTIVE` or `WARNING`, and matches its identity-certificate fingerprint; and
7. extracts the certificate public key for the Valiron request proof.

The timestamp, nonce, request digest, and `valiron-agent-request-v1` payload are
a Valiron transport-proof envelope. They are not presented as an ANS-native HTTP
signature standard.

Valiron manages provider credentials and trust configuration on the hosted service. Customers supply signed proofs, not provider secrets.

If the hosted resolver is unavailable, ANS claims return `claimed` with `failureReason: "resolver_unavailable"`.
Malformed, inactive, revoked, mismatched, untrusted, or certificate-less ANS
records cannot become `verified`.

This first adapter does not independently validate live DNS/DANE state, a SCITT
receipt, Merkle inclusion, checkpoint signatures, OCSP, or CRLs. Those are
additional ANS integrity checks, not claims made by this endpoint. There is no
domain-registration feature in Valiron.

Public references:

- <https://www.godaddy.com/en-in/ans/developers>
- <https://docs.developer.commerce.godaddy.com/en/docs/references/rest/ans/resolution>
- <https://github.com/agentnameservice/ans-registry>
- <https://github.com/agentnameservice/ans-sdk-go>
- <https://www.rfc-editor.org/rfc/rfc5280#section-6>
