# 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 | — | API key (optional for read operations) |
| `endpoint` | string | `https://valiron-edge-proxy.onrender.com` | API base URL |
| `chain` | SupportedChain | `"ethereum"` | Default blockchain network |
| `timeout` | number | `5000` | Request timeout in milliseconds |
| `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({ 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({ 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({ chain: "solana" });
const profile = await valiron.getWalletProfile("VSAnVX4MQjEXreHc92iHxWuXPQZ1tjwiKccpFLCgLM9");
```

---

### 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).

Resolution chain: Redis → in-memory cache → Agent0 subgraph → null.

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({ chain: "solana" });
const resolution = await valiron.resolveWallet("VSAnVX4MQjEXreHc92iHxWuXPQZ1tjwiKccpFLCgLM9");
// resolution.source will be "solana" instead of "redis" | "subgraph"
```

---

### 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?)

Trust gate — single call for allow/deny decisions. Combines on-chain reputation and sandbox tests. Designed for payment protocol protection (x402, etc.).

```typescript
const result = await valiron.gate(agentId: string, options?: {
  ttlMs?: number;       // default: 86400000 (24h)
  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;
}
```

**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).

---

### createValironGate(config)

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

Uses the public `getAgentProfile()` endpoint under the hood (not the protected `gate()` endpoint), so it works from any server context — no agent challenge-response auth required.

**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
  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({ chain: "monad" });

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

// Custom threshold + longer cache
app.use("/api/premium", createValironGate({
  sdk: valiron,
  minScore: 85,
  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) are automatically evaluated when they first hit a gated endpoint. The middleware triggers `triggerKeyAgentSandbox()` in the background and returns 403 with `retryAfterMs: 30000`. After evaluation completes, the agent receives a trust score and can access endpoints normally.

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
```

### KeyAgentProfile

| Field | Type | Description |
|-------|------|-------------|
| `address` | string | Ethereum address |
| `isKeyAgent` | boolean | Always `true` |
| `tier` | string | Trust tier (starts at C, can improve) |
| `riskLevel` | string | GREEN / YELLOW / RED |
| `route` | string | Route decision |
| `firstSeen` | string | ISO timestamp of first interaction |
| `totalRequests` | number | Lifetime request count |

---

## 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<WorldIdVerificationResult>;
```

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

### getWorldIdStatus(agentId, options?)

Check if an agent has a verified World ID.

```typescript
const status = await valiron.getWorldIdStatus(agentId: string, options?: {
  chain?: SupportedChain;
}): Promise<WorldIdStatus>;
// { verified: true, verifiedAt: "2025-01-15T...", level: "orb" }
```

### getWorldIdProfile(agentId, options?)

Get full World ID profile including verification details.

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

---

## Telemetry

The SDK optionally collects anonymous usage metrics (gate calls, latency, error rates). Telemetry is opt-in and can be fully disabled.

### TelemetryConfig

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `enabled` | boolean | `true` | Enable/disable telemetry |
| `sampleRate` | number | `1.0` | Fraction of events to sample (0–1) |
| `handler` | function | — | Custom event handler |

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

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

---

## Middleware

All middleware adapters use `getAgentProfile()` (public endpoint) rather than
`gate()` (protected endpoint), so they work from any server context without
agent challenge-response auth. Results are cached client-side (default 30s)
to survive x402's two-request payment flow and avoid cold-start failures.

### 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({ chain: "monad" });

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

### createValironNextMiddleware — Next.js

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

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

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

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

const valiron = new ValironSDK();

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();
});
```

### Client-Side Caching

All middleware adapters include a built-in in-memory cache (default TTL: 30 seconds).
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:

| Status | When | Body |
|--------|------|------|
| 401 | No agent ID found in request | `{ error: "No agent identity provided" }` |
| 403 | Agent denied by trust gate | `{ error: "Access denied", tier, riskLevel, route }` |
| 403 | New agent pending evaluation | `{ error: "Agent pending evaluation", retryAfterMs: 30000 }` |
| 503 | Valiron service unavailable | `{ error: "Trust evaluation unavailable" }` |

> **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 |

### 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,
  isValidAddress,
  generateChallenge,
} from "@valiron/sdk";
```

---

## Plans & Limits

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

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