# SDK Reference

Package: `@valiron/sdk`

## Installation

```bash
npm install @valiron/sdk
# or
pnpm add @valiron/sdk
```

## Constructor

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

const valiron = new ValironSDK(config?: ValironConfig);
```

### ValironConfig

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `apiKey` | string | — | Operator API key. Required for profile/status reads, trust gates, and sandbox runs; Pro is required for paid signals, enriched profiles, proxy calls, and webhooks |
| `endpoint` | string | `https://valiron-edge-proxy.onrender.com` | API base URL |
| `chain` | SupportedChain | `"ethereum"` | Default blockchain network |
| `timeout` | number | method-aware | Request timeout in milliseconds. Defaults are 10s generally, 15s for `gate()`/`proxy()`, and 30s for sandbox triggers. If set, this value overrides all method-aware defaults. |
| `debug` | boolean | `false` | Enable debug logging |
| `telemetry` | TelemetryConfig | — | Optional telemetry configuration |

### SupportedChain

```typescript
type SupportedChain =
  | "ethereum" | "monad" | "arbitrum" | "base" | "avalanche"
  | "celo" | "polygon" | "linea" | "abstract" | "bsc"
  | "gnosis" | "goat" | "mantle" | "megaeth" | "metis"
  | "optimism" | "scroll" | "skale_base" | "soneium"
  | "taiko" | "xlayer" | "base_sepolia" | "ethereum_sepolia"
  | "abstract_testnet" | "arbitrum_sepolia" | "avalanche_testnet"
  | "bsc_testnet" | "celo_testnet" | "linea_sepolia"
  | "mantle_testnet" | "megaeth_testnet" | "metis_sepolia"
  | "optimism_sepolia" | "polygon_amoy" | "scroll_sepolia"
  | "skale_base_sepolia" | "soneium_minato" | "taiko_hoodi"
  | "xlayer_testnet" | "hedera_testnet" | "arc_testnet"
  | "solana";
```

> **Solana:** When using `chain: "solana"`, agent IDs are base-58 Metaplex Core
> asset pubkeys (not numeric). Wallet addresses are also base-58.
> See [CHAINS.md](./CHAINS.md#solana) for details.

---

## Methods

### checkAgent(agentId, options?)

Quick routing check. Returns only the route decision string.

```typescript
const route = await valiron.checkAgent(agentId: string, options?: {
  chain?: SupportedChain;
}): Promise<RouteDecision>;
```

**Returns**: `"prod"` | `"prod_throttled"` | `"sandbox"` | `"sandbox_only"`

**Example**:
```typescript
const route = await valiron.checkAgent("25459");
if (route === "prod") {
  // Agent is trusted — proceed with full access
}
```

**Solana example**:
```typescript
const valiron = new ValironSDK({ apiKey: process.env.VALIRON_API_KEY, chain: "solana" });

// By asset pubkey
const route = await valiron.checkAgent("DFkntsJ9aTCHkK88m6WZVQEvLdLNi4X1LVUgRMqetFPM");

// By sequential ID
const route = await valiron.checkAgent("1226");
```

---

### getAgentProfile(agentId, options?)

Full trust profile combining on-chain identity, reputation, and routing.

```typescript
const profile = await valiron.getAgentProfile(agentId: string, options?: {
  chain?: SupportedChain;
}): Promise<AgentProfile>;
```

**Returns**: `AgentProfile`

```typescript
interface AgentProfile {
  agentId: string;
  identity: AgentIdentity;
  onchainReputation: OnchainReputation;
  routing: {
    finalRoute: RouteDecision;
    meetsThreshold: boolean;
  };
  chain: ChainContext;
  timestamp: string;
}
```

**Example**:
```typescript
const profile = await valiron.getAgentProfile("25459");
console.log(profile.routing.finalRoute);       // "prod"
console.log(profile.routing.meetsThreshold);   // true
console.log(profile.onchainReputation.averageScore); // 92.5
```

**Solana example**:
```typescript
const valiron = new ValironSDK({ apiKey: process.env.VALIRON_API_KEY, chain: "solana" });
const profile = await valiron.getAgentProfile("1226"); // sequential ID works

console.log(profile.identity.name);                      // "NoahScout_Bot.noah"
console.log(profile.onchainReputation.solana?.trustTier); // "trusted"
console.log(profile.onchainReputation.solana?.qualityScore); // 85
```

---

### getWalletProfile(wallet, options?)

Reverse-lookup a wallet address to get its trust profile.

```typescript
const profile = await valiron.getWalletProfile(wallet: string, options?: {
  chain?: SupportedChain;
}): Promise<WalletProfile>;
```

**Example**:
```typescript
const profile = await valiron.getWalletProfile("0x52ce...");
console.log(profile.routing.finalRoute);
```

**Solana example**:
```typescript
const valiron = new ValironSDK({ apiKey: process.env.VALIRON_API_KEY, chain: "solana" });
const profile = await valiron.getWalletProfile("VSAnVX4MQjEXreHc92iHxWuXPQZ1tjwiKccpFLCgLM9");
```

---

### getWalletRisk(wallet, options?)

Return an explainable wallet-history assessment. This is a Pro feature and requires an operator API key.

```typescript
const valiron = new ValironSDK({ apiKey: process.env.VALIRON_API_KEY });
const assessment = await valiron.getWalletRisk("0x52ce...", { chain: "base" });

if (assessment.available) {
  console.log(assessment.score);      // 0-100 trust score; higher is better
  console.log(assessment.risk);       // 100 - score
  console.log(assessment.riskLevel);  // "LOW" | "MEDIUM" | "HIGH" | "CRITICAL"
  console.log(assessment.confidence); // 0-1 history coverage confidence
  console.log(assessment.reasons);
} else {
  console.log(assessment.error);      // missing key, unsupported network, timeout, etc.
}
```

Supported history networks are Ethereum, Base, Arbitrum, Optimism, Polygon, and Solana. EVM uses Alchemy; Solana uses Helius. The assessment uses bounded samples rather than complete transaction history. It does not provide sanctions, mixer, scam, or criminal attribution without an additional intelligence provider.

---

### resolveWallet(wallet, options?)

Lightweight wallet → agentId resolution without the full trust profile. Useful when you only need to know which agent owns a wallet (e.g. after extracting a wallet from an x402 payment header).

Wallet resolution returns the associated agent ID when available.

Signature: `resolveWallet(wallet: string, options?: { chain?: SupportedChain }): Promise<WalletResolution>`

**Returns**: `WalletResolution`

```typescript
interface WalletResolution {
  wallet: string;
  agentId: string | null;
  source: "redis" | "subgraph" | "cache" | "none";
  chainId?: number | null;
  agentName?: string | null;
  timestamp: string;
}
```

**Example**:
```typescript
const resolution = await valiron.resolveWallet("0x52ce...");
if (resolution.agentId) {
  const profile = await valiron.getAgentProfile(resolution.agentId);
}
```

**Solana example**:
```typescript
const valiron = new ValironSDK({ apiKey: process.env.VALIRON_API_KEY, chain: "solana" });
const resolution = await valiron.resolveWallet("VSAnVX4MQjEXreHc92iHxWuXPQZ1tjwiKccpFLCgLM9");
// Solana matches return source "solana_registry"; no match returns "none".
// The SDK 1.4 ResolutionSource type does not yet include "solana_registry".
```

---

### triggerSandboxTest(agentId, options?)

Run sandbox tests against an agent and compute a trust evaluation.

```typescript
const result = await valiron.triggerSandboxTest(agentId: string, options?: {
  chain?: SupportedChain;
}): Promise<SandboxResult>;
```

**Returns**: `SandboxResult`

```typescript
interface SandboxResult {
  ok: boolean;
  agentId: string;
  wallet: string;
  tier: MoodysRating;          // "AAA" | "AA" | "A" | "BAA" | "BA" | "B" | "CAA" | "CA" | "C"
  riskLevel: "GREEN" | "YELLOW" | "RED";
  meetsThreshold: boolean;
  testSummary: object;         // Opaque test metrics (structure is proprietary)
  chain: ChainContext;
  message: string;
}
```

**Example**:
```typescript
const result = await valiron.triggerSandboxTest("25459");
console.log(result.tier);            // "AAA"
console.log(result.riskLevel);       // "GREEN"
console.log(result.meetsThreshold);  // true
```

> **Auto-sandbox:** The middleware (`createValironGate`, `valironFastifyPlugin`, `createValironNextMiddleware`, `valironGateCheck`) automatically triggers `triggerSandboxTest()` in the background for new on-chain agents (those with `totalFeedback === 0`). The agent receives a 403 with `{ error: "Agent pending evaluation", retryAfterMs: 30000 }` while evaluation runs. No manual trigger is needed.

---

### triggerKeyAgentSandbox(agentAddress)

Run sandbox tests against a key-based (Web2) agent and compute a trust evaluation. Key agents don't have on-chain endpoints, so this uses the `sandbox_relay` mode internally.

```typescript
const result = await valiron.triggerKeyAgentSandbox(agentAddress: string): Promise<KeyAgentSandboxResult>;
```

**Returns**: `KeyAgentSandboxResult`

```typescript
interface KeyAgentSandboxResult {
  ok: boolean;
  agentAddress: string;
  valironScore: number;
  tier: MoodysRating;
  riskLevel: "GREEN" | "YELLOW" | "RED";
  message: string;
}
```

**Example**:
```typescript
const result = await valiron.triggerKeyAgentSandbox("0x1234...abcd");
console.log(result.tier);            // "A"
console.log(result.riskLevel);       // "GREEN"
console.log(result.valironScore);    // 85
```

> **Auto-sandbox:** The middleware automatically triggers `triggerKeyAgentSandbox()` in the background for new key agents (those with `verified === true` but `score === null`). The agent receives a 403 with `{ error: "Agent pending evaluation", retryAfterMs: 30000 }` while evaluation runs.

---

### gate(agentId, options?)

Low-level hosted gate combining on-chain reputation and sandbox tests. The HTTP endpoint requires both an operator API key and agent challenge-response headers. SDK 1.4 `gate()` does not forward agent-session headers, so an API key alone is insufficient. For server integrations, use `createValironGate()` or `valironGateCheck()`; for direct HTTP calls, follow the [authentication flow](https://www.valiron.co/auth.md).

```typescript
const result = await valiron.gate(agentId: string, options?: {
  minScore?: number;     // default: 65
  ttlMs?: number;       // default: 86400000 (24h)
  trustSignals?: Array<"8004" | "sandbox" | "world" | "self" | "icebreaker" | "wallet">;
  chain?: SupportedChain;
}): Promise<GateResult>;
```

**Returns**: `GateResult`

```typescript
interface GateResult {
  allow: boolean;
  tier: MoodysRating;
  riskLevel: "GREEN" | "YELLOW" | "RED";
  meetsThreshold: boolean;
  route: RouteDecision;
  agentId: string;
  wallet: string;
  chain: ChainContext;
  sandboxRan: boolean;
  cached: boolean;
  walletRisk?: WalletRiskSnapshot;
}
```

**Example**:
```typescript
const result = await valiron.gate("25459");

if (result.allow) {
  // proceed with payment
} else {
  console.log(`Agent denied — tier: ${result.tier}, risk: ${result.riskLevel}`);
}
```

---

### getAgentSnapshot(agentId, options?)

Get the latest behavioral snapshot hash for an agent. Returns an opaque hash of the most recent behavioral evaluation — designed for hash-chain commitment systems that need to commit behavioral state on-chain without accessing raw metrics.

```typescript
const snapshot = await valiron.getAgentSnapshot(agentId: string, options?: {
  chain?: SupportedChain;
}): Promise<AgentSnapshotSummary>;
```

**Returns**: `AgentSnapshotSummary`

```typescript
interface AgentSnapshotSummary {
  agentId: string;
  snapshotHash: string;          // deterministic SHA-256 hash of behavioral evaluation (opaque)
  previousHash: string;          // hash from prior evaluation, or "0x0" for first
  encryptedDataUri: string | null; // IPFS CID of encrypted snapshot data, if available
  timestamp: string | null;      // ISO 8601 timestamp of evaluation
  interactionCount: number;      // total interactions in the evaluation window
}
```

**Example**:
```typescript
const snapshot = await valiron.getAgentSnapshot("25459");
console.log(snapshot.snapshotHash);       // "0x3a7f..."
console.log(snapshot.previousHash);       // "0x0" (genesis) or "0x8b2c..."
console.log(snapshot.interactionCount);   // 15
```

**Notes**:
- The `snapshotHash` is computed internally by Valiron from behavioral metrics. It is opaque — you cannot reverse it to raw metrics.
- Each hash references the `previousHash`, forming a chain. First evaluation gets `"0x0"`.
- Trigger a sandbox evaluation first if no snapshot exists (returns 404 otherwise).

---

### verifyAgentIdentity(proof, policy?)

Verifies an ANS or other provider-backed identity, loads Valiron reputation,
and returns the requested policy decision.

```typescript
const identity = await valiron.verifyAgentIdentity(proof, {
  anonymous: "challenge",
  claimed: "block",
  verified: "allow",
  minReputationScore: 65,
  belowMinimumScore: "limit",
  highRisk: "block",
});
```

For protected API routes, prefer the provider identity middleware below. It
calculates `requestDigest` from the exact received bytes. Direct callers are
responsible for binding the proof and decision to those same bytes.

See [Provider-Backed Agent Identity](./PROVIDER-IDENTITY.md) for proof and result
types, signing helpers, identity statuses, and policy semantics.

---

### createValironGate(config)

Express-compatible middleware factory. Automatically gates requests based on agent trust.

Uses `getAgentProfile()` with your server-side operator API key. The middleware does not perform a fresh agent challenge-response; bind the claimed agent identity to your authenticated caller before authorizing a sensitive action.

**Express 4 safe**: The returned middleware wraps the async handler with `Promise.resolve(handler(...)).catch(next)` so async rejections are properly forwarded to Express's error handler. This prevents race conditions when chained with other async middleware (e.g. x402's `paymentMiddleware`).

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

const gate = createValironGate(config: {
  sdk: ValironSDK;
  minScore?: number;          // default: 65
  trustSignals?: Array<"8004" | "sandbox" | "world" | "self" | "icebreaker" | "wallet">;
  cacheTtlMs?: number;        // default: 30000 (30s client-side cache)
  extractAgentId?: (req: Request) => string | null;
  extractAgentAddress?: (req: Request) => string | null;
  onAllow?: (req: Request, result: GateResult) => void;
  onDeny?: (req: Request, res: Response, result: GateResult) => void;
});
```

**Default behavior**:
- Extracts agent ID from `x-agent-id` header (on-chain) or `x-agent-address` header (key-based)
- Returns 401 if no agent identity is found (with link to identity skill)
- Returns 403 if agent is denied
- Returns 503 if the trust gate is unreachable (fail-closed)
- Caches profiles in memory for 30s to survive x402 retry flows

**Example**:
```typescript
const valiron = new ValironSDK({ apiKey: process.env.VALIRON_API_KEY, chain: "monad" });

// Basic gate
app.use("/api/paid", createValironGate({ sdk: valiron }));

// Custom threshold + longer cache
app.use("/api/premium", createValironGate({
  sdk: valiron,
  minScore: 85,
  trustSignals: ["8004", "sandbox"],
  cacheTtlMs: 60_000, // 60s
}));

// Custom agent ID extraction
app.use("/api/custom", createValironGate({
  sdk: valiron,
  extractAgentId: (req) => req.query.agent_id as string,
  onDeny: (req, res, result) => {
    res.status(403).json({
      error: "Insufficient trust",
      currentTier: result.tier,
      riskLevel: result.riskLevel,
    });
  },
}));
```

---

### dispose()

Flush pending telemetry and clean up resources.

```typescript
await valiron.dispose(): Promise<void>;
```

Call this when shutting down long-running processes.

---

## Proxy Gateway (Pro)

Forward requests through Valiron's edge proxy with automatic trust gating, logging, and SSRF protection.

### proxy(request)

```typescript
const result = await valiron.proxy(request: ProxyRequest): Promise<ProxyResponse>;
```

#### ProxyRequest

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `agentId` | string | yes | Agent to gate |
| `targetUrl` | string | yes | Your origin URL to forward to |
| `method` | string | no | HTTP method (default: `GET`) |
| `headers` | Record<string, string> | no | Headers to forward |
| `body` | string | no | Request body |

#### ProxyResponse

| Field | Type | Description |
|-------|------|-------------|
| `statusCode` | number | HTTP status from your origin |
| `responseHeaders` | Record<string, string> | Response headers |
| `responseBody` | string | Response body |
| `responseMs` | number | Round-trip latency |
| `costCharged` | string | USD cost charged |
| `route` | string | Route decision applied |
| `tierResolved` | string | Trust tier used |

**Example**:
```typescript
const result = await valiron.proxy({
  agentId: "25459",
  targetUrl: "https://your-api.com/data",
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ query: "example" }),
});
```

> **SSRF Protection:** The proxy blocks requests to private IPs (10.x, 172.16-31.x, 192.168.x), localhost, link-local addresses, and cloud metadata endpoints (169.254.169.254).

---

## Key-Based Agents

For Web2 agents that don't have on-chain identities, Valiron supports key-based authentication using EIP-191 challenge-response.

**How it works:**
1. Agent sends a request with `x-agent-address: 0x...` header
2. Valiron returns a challenge nonce
3. Agent signs the nonce with their private key
4. Valiron verifies the signature and creates a `KeyAgentProfile`

The SDK auto-detects key-based agents from the `x-agent-address` header. No configuration needed.

### Sandbox Evaluation for Key Agents

New key agents (verified but not yet scored) receive 403 with `retryAfterMs: 30000`. The middleware attempts to trigger `triggerKeyAgentSandbox()` in the background. This run requires a valid operator API key; if it fails, the agent remains pending until an operator starts an evaluation. After evaluation completes, the agent receives a trust score; middleware allows the request when `verified === true` and the score meets the endpoint's `minScore`.

The current key-only sandbox relay does not observe the agent's own HTTP
behavior. Do not use that score alone to authorize a high-value purchase or
payout. The Web2 Icebreaker demo uses signed key ownership and reports
`behavioralScore: null` when no real behavioral evidence exists.

You can also trigger evaluation manually:

```typescript
const result = await valiron.triggerKeyAgentSandbox("0x1234...abcd");
```

Or via HTTP:

```bash
curl -X POST https://valiron-edge-proxy.onrender.com/operator/trigger-sandbox-key/0x1234...abcd -H "Authorization: Bearer $VALIRON_API_KEY"
```

### KeyAgentProfile

| Field | Type | Description |
|-------|------|-------------|
| `agentAddress` | string | Ethereum-style address used as the key-based agent identifier |
| `verified` | boolean | Whether the agent proved key ownership through challenge-response |
| `score` | number \| null | Behavioral score once sandbox evaluation has run |
| `tier` | string \| null | Moody's-style trust tier once scored |
| `riskLevel` | string \| null | GREEN / YELLOW / RED once scored |
| `route` | string \| null | Route decision once scored |
| `icebreaker` | object \| null | Redacted from the summary profile; use `getKeyAgentIcebreaker()` with a Pro key |
| `reasons` | string[] | Redacted from the summary profile |
| `timestamp` | string | ISO timestamp for the profile response |

```typescript
const valiron = new ValironSDK({ apiKey: process.env.VALIRON_API_KEY });
const { icebreaker } = await valiron.getKeyAgentIcebreaker(agentAddress);
```

The private Icebreaker result contains the attestation state, owner, verified
social handles, organization, and profile and credential URLs. It does not
contain social connections or mutuals, and does not alter the Web2 behavioral
score automatically.

---

## World ID Methods

Verify agent-human linkage via Worldcoin's zero-knowledge proof system.

### verifyWorldId(agentId, proof, options?)

Submit a World ID proof for verification.

```typescript
const result = await valiron.verifyWorldId(agentId: string, proof: WorldIdProof, options?: {
  chain?: SupportedChain;
}): Promise<{ ok: boolean; agentId: string; worldId: WorldIdStatus; timestamp: string }>;
```

**Example**:
```typescript
const result = await valiron.verifyWorldId("25459", {
  merkle_root: "0x...",
  nullifier_hash: "0x...",
  proof: "0x...",
  verification_level: "orb",
});
```

### getWorldIdStatus(agentId, options?)

Check if an agent has a verified World ID.

```typescript
const status = await valiron.getWorldIdStatus(agentId: string, options?: {
  chain?: SupportedChain;
}): Promise<{ agentId: string; worldId: WorldIdStatus; timestamp: string }>;
// status.worldId: { verified: true, verificationLevel: "orb" }
```

### getWorldIdProfile(agentId, options?)

Get the enriched World ID profile with an allowlisted verification summary. Requires a Pro operator key.

```typescript
const profile = await valiron.getWorldIdProfile(agentId: string, options?: {
  chain?: SupportedChain;
}): Promise<WorldIdAgentProfile>;
```

---

## Self Methods

Self provides eligible-human verification from government ID credentials and can optionally bind that verified human to Self Agent ID. Valiron stores the verified Self predicates as the selectable `self` trust signal.

### createSelfSession(agentId, request, options?)

Create a Self Enterprise verification session for an agent. Redirect the user to the returned `verificationUrl`; Self will call Valiron's signed webhook after completion.

```typescript
const session = await valiron.createSelfSession("25459", {
  selfAgentId: "self-agent-...",
  successUrl: "https://example.com/verified",
  failureUrl: "https://example.com/failed",
});
console.log(session.self.verificationUrl);
```

### getSelfStatus(agentId, options?)

Check if an agent has a verified Self human eligibility link.

```typescript
const { self } = await valiron.getSelfStatus("25459");
console.log(self.verified, self.selfAgentLinked);
```

### getSelfProfile(agentId, options?)

Get the enriched Self profile with eligibility and agent-binding predicates. Requires a Pro operator key; provider identifiers are omitted.

```typescript
const profile = await valiron.getSelfProfile("25459");
console.log(profile.humanTrust.hasEligibleHuman);
console.log(profile.humanTrust.hasAgentBinding);
```

### verifySelf(agentId, proof, options?)

Deprecated; retained for compatibility until the next major SDK release. Not supported for hosted customer integrations. Use `createSelfSession()`, send the user to the returned verification URL, and check `getSelfStatus()` after verification. Valiron receives the signed result from Self.

---

## Telemetry

The SDK collects opt-out usage telemetry to improve reliability. Events include SDK version, runtime, environment, method names, chain labels, latency, HTTP status/error buckets, route decisions, and coarse score/reputation buckets. Raw agent IDs, wallet addresses, API keys, and exact scores are not sent in SDK telemetry. Operator paywall usage logs are separate customer analytics and are authenticated with the operator API key.

SDK reports are anonymous client reports and may be sampled. Signal counts describe reported selections, not successful verifications. Version comes from the shipped package metadata. Environment defaults to recognized `NODE_ENV` values (`production` → `prod`, `development` → `dev`, `test` → `test`), otherwise `unknown`; set it explicitly for browser applications. Telemetry observer failures do not affect API calls.

### TelemetryConfig

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `enabled` | boolean | `true` | Enable/disable telemetry |
| `sampleRate` | number | `1.0` | Fraction of events to sample (0–1) |
| `env` | prod / staging / dev / test / unknown | NODE_ENV or unknown | Environment label |
| `onEvent` | function | — | Custom event handler; failures are isolated from API calls |

**Example**:
```typescript
const valiron = new ValironSDK({
  apiKey: process.env.VALIRON_API_KEY,
  telemetry: {
    enabled: false,         // Disable entirely
  },
});

// Or with custom sampling and handler
const valiron = new ValironSDK({
  apiKey: process.env.VALIRON_API_KEY,
  telemetry: {
    sampleRate: 0.1,        // Sample 10% of events
    onEvent: (event) => {   // Custom handler
      myAnalytics.track(event);
    },
  },
});
```

---

## Middleware

Trust middleware adapters use authenticated profile reads to evaluate route policy locally. Configure the SDK with your server-side operator API key and bind the agent identifier to your authenticated caller. These adapters do not prove possession of the caller's key; provider identity middleware verifies signed proofs separately. Profile results are cached client-side for 30 seconds by default.

### createValironGate(config) — Express

See above for full documentation.

**Express 4 note**: The returned middleware uses a synchronous wrapper
(`Promise.resolve().catch(next)`) to ensure async errors are properly caught.
You do NOT need to wrap it yourself — it's safe to use directly with
`app.use()` even on Express 4.

### valironFastifyPlugin — Fastify

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

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

fastify.register(valironFastifyPlugin, {
  sdk: valiron,
  prefix: "/api/paid",
});
```

### createValironNextMiddleware — Next.js

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

const valiron = new ValironSDK({ apiKey: process.env.VALIRON_API_KEY });
export default createValironNextMiddleware({
  sdk: valiron,
  paths: ["/api/paid"],
});
```

### valironGateCheck — Generic (Hono, Koa, etc.)

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

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

app.use(async (c, next) => {
  const gate = await valironGateCheck(valiron, {
    agentId: c.req.header("x-agent-id") ?? null,
    agentAddress: c.req.header("x-agent-address") ?? null,
  });
  if (!gate.allow) return c.json(gate.body, gate.status);
  c.set("valiron", gate.result);
  await next();
});
```

### Provider identity middleware — ANS and future adapters

Provider identity uses dedicated, non-caching gates because every request has
a fresh timestamp and nonce:

```typescript
import {
  createValironIdentityGate,
  valironIdentityFastifyPlugin,
  createValironIdentityNextMiddleware,
  valironIdentityGateCheck,
} from "@valiron/sdk";
```

`createValironIdentityGate({ sdk, policy, getRawBody? })` protects Express and
Connect routes. `valironIdentityFastifyPlugin` provides the equivalent Fastify
`preHandler`; `createValironIdentityNextMiddleware` hashes a cloned Fetch
request; and `valironIdentityGateCheck` accepts explicit proof fields and exact
body bytes for Hono, Koa, Bun, and custom runtimes.

The default transport uses `x-valiron-identity-provider`,
`x-valiron-identity-agent-id`, `x-valiron-identity-signature`,
`x-valiron-identity-timestamp`, `x-valiron-identity-nonce`, and optional
`x-valiron-identity-key-id`. The request digest is never accepted from a header;
the gate calculates it locally. See
[Provider-Backed Agent Identity](./PROVIDER-IDENTITY.md#api-owner-enforce-an-identity-policy)
for complete Express and agent examples.

Policy actions map to HTTP status as follows:

| Action | Middleware behavior |
| --- | --- |
| `allow` | Continue and attach `valironIdentity` to the request |
| `challenge` | HTTP 401 with `WWW-Authenticate: ValironIdentity` |
| `block` | HTTP 403 |
| `limit` | HTTP 429 with `Retry-After: 60` |

Unexpected verification failures return 503 and fail closed. Express and
Fastify return `IDENTITY_RAW_BODY_REQUIRED` with HTTP 400 when a body was parsed
without preserving its exact bytes.

### Client-Side Caching

Trust middleware adapters include a built-in in-memory cache (default TTL: 30 seconds). Provider identity gates do not cache request proofs.
This is important for payment protocols like x402 that make two requests per payment
(discover 402 → retry with payment). Without caching, the second trust check can fail
due to cold-start latency on the edge proxy.

The Express gate exposes `cacheTtlMs` to override this TTL; set it to `0` to disable
caching for that middleware instance. `createValironNextMiddleware` and
`valironGateCheck` also accept `cacheTtlMs`.

### Middleware Error Responses

All middleware variants return consistent error responses. Each body includes a stable `code`, a human-readable `message`, an `action`, and a `docsUrl` in addition to the trust result fields.

| Status | Code | When |
|--------|------|------|
| 401 | `MISSING_AGENT_IDENTITY` | No agent identity found in request |
| 403 | `TRUST_GATE_DENIED` | Agent denied by trust gate; inspect `score`, `tier`, `route`, and `reasons` |
| 403 | `AGENT_EVALUATION_PENDING` | New agent pending evaluation; retry after `retryAfterMs` |
| 503 | `TRUST_GATE_UNAVAILABLE` | Valiron service unavailable; fail closed and retry shortly |

> **Fail-closed:** When the Valiron service is unreachable, middleware returns 503 — it never fails open and never allows unverified agents through.

---

## Response Types

### GateResult

Returned by `gate()` and middleware.

| Field | Type | Description |
|-------|------|-------------|
| `allow` | boolean | Whether the agent is allowed access |
| `tier` | string | Trust tier (AAA–C) |
| `riskLevel` | string | GREEN / YELLOW / RED |
| `route` | string | `prod` / `prod_throttled` / `sandbox` / `sandbox_only` |
| `meetsThreshold` | boolean | Whether the agent meets the minimum trust threshold |
| `agentId` | string | The evaluated agent ID |
| `wallet` | string | Agent's wallet address |
| `chain` | object | Chain context |
| `sandboxRan` | boolean | Whether sandbox was executed |
| `cached` | boolean | Whether the result was served from cache |
| `walletRisk` | WalletRiskSnapshot? | Wallet assessment when the `wallet` signal was requested |

### WalletRiskSnapshot

Returned by `getWalletRisk()` and optionally included in agent profiles and gate results.

| Field | Type | Description |
|-------|------|-------------|
| `available` | boolean | Whether provider-backed history was available |
| `wallet` | string | Normalized wallet address |
| `network` | SupportedChain | Evaluated network |
| `provider` | `alchemy` \| `helius` \| `none` | History provider used |
| `score` | number | Confidence-damped trust score, 0-100; higher is better |
| `rawScore` | number | Score before confidence damping |
| `risk` | number | `100 - score` |
| `confidence` | number | History-coverage confidence from 0-1 |
| `riskLevel` | string | LOW / MEDIUM / HIGH / CRITICAL |
| `reasons` | string[] | Explainable scoring reasons |
| `hardFlags` | string[] | Hard-risk flags; any populated flag forces `sandbox_only` in gates |
| `features` | WalletHistoryFeatures | Normalized history measurements |
| `observedAt` | string | ISO observation timestamp |
| `expiresAt` | string | ISO cache-expiry timestamp |
| `error` | string? | Provider/configuration failure when unavailable |

### AgentProfile

Returned by `getAgentProfile()`.

| Field | Type | Description |
|-------|------|-------------|
| `agentId` | string | ERC-8004 token ID |
| `identity` | object | On-chain identity (name, wallet, endpoints, trustModels) |
| `onchainReputation` | object | Feedback count, average score, Solana ATOM metrics |
| `routing` | object | `{ finalRoute, meetsThreshold }` |
| `chain` | object | `{ name, chainId }` |
| `timestamp` | string | ISO timestamp |

### Request Headers

Headers attached to proxied/gated requests:

| Header | Description |
|--------|-------------|
| `x-agent-id` | The agent's ERC-8004 token ID |
| `x-agent-address` | The agent's wallet address (key-based agents) |
| `x-valiron-tier` | Resolved trust tier |
| `x-valiron-route` | Route decision |
| `x-valiron-risk` | Risk level |

### All Exports

```typescript
import {
  ValironSDK,
  ValironOperator,
  ValironClient,
  createValironGate,
  valironFastifyPlugin,
  createValironNextMiddleware,
  valironGateCheck,
  createValironIdentityGate,
  valironIdentityFastifyPlugin,
  createValironIdentityNextMiddleware,
  valironIdentityGateCheck,
  createAgentIdentityProof,
  agentIdentityProofHeaders,
  sha256RequestDigest,
  buildAgentIdentitySigningPayload,
  isValidAddress,
  generateChallenge,
  type WalletRiskSnapshot,
  type WalletRiskLevel,
} from "@valiron/sdk";
```

---

## Plans & Limits

| Feature | Free | Pro |
|---------|------|-----|
| Gated endpoints | 3 | Unlimited |
| Sandbox tests / day | 5 | Unlimited |
| Webhooks | — | ✓ |
| Proxy gateway | — | ✓ |
| Analytics & logs | — | ✓ |
| Custom tier thresholds | — | ✓ |
| Wallet intelligence | — | ✓ |

Pro plan requires an operator API key (`val_op_` prefix) from the [Operator Dashboard](./DASHBOARD.md).
