Valiron Docs
Valiron provides trust middleware for autonomous agent traffic. It evaluates agent identity, reputation, and behavior at the application boundary, enabling policy-based access control before request handling.
Installation
npm install @valiron/sdk
# or
pnpm add @valiron/sdkGetting Started
The primary integration path is SDK middleware deployed within the API backend. Middleware evaluates each identified agent and returns a deterministic route decision before the application handler processes the request.
import { ValironSDK } from '@valiron/sdk';
const valiron = new ValironSDK();
// Quick routing check (defaults to Ethereum)
const route = await valiron.checkAgent('25459');
if (route === 'prod') {
// Allow full production access
}
// Query a specific chain
const monadRoute = await valiron.checkAgent('8348', { chain: 'monad' });
const baseRoute = await valiron.checkAgent('8348', { chain: 'base' });
// Or set default chain at construction
const monadValiron = new ValironSDK({ chain: 'monad' });
const profile = await monadValiron.getAgentProfile('8348');
console.log(profile.routing.finalRoute); // 'prod'
console.log(profile.localReputation?.tier); // 'AAA'
console.log(profile.onchainReputation.averageScore); // 92.5Operator Dashboard
The Operator Dashboard at valiron.co/dashboard is where you manage your Valiron middleware integration — generate API keys, test trust evaluations, and monitor agent activity.
Getting Started
- Create your account at
/dashboard— enter your name, email, and password - Copy your API key — shown once at registration (
val_op_prefix) - Test trust decisions — use the playground before adding middleware to production routes
- Monitor traffic — inspect agent activity, usage, errors, and cost once your middleware is live
API Playground
Test how Valiron evaluates different AI agents against your endpoints — without writing code or sending real traffic. The playground answers: "If an agent with this reputation hits my endpoint, what happens?"
- Pick a chain — All supported chains (Ethereum, Solana, Monad, Arbitrum, Base, Avalanche, Polygon, Optimism, and more). Chains with pre-registered sample agents show agent cards; other chains require a custom agent ID.
- Select an agent — Sample agents at different trust levels (good / bad / zero reputation) or enter a custom ERC-8004 agent ID
- Build a request — Method and path. Required headers are injected automatically. Expand "Advanced options" for custom headers, query params, or body
- Send — See the trust evaluation, routing decision, tier, cost, and full response
Dry-run mode returns the trust evaluation (tier, route, price, on-chain score) without logging the call. With dry-run off, the playground performs the full evaluation and logs it to your Analytics & Logs pages. If your endpoint has an upstream API base URL, the request is forwarded to your backend with trust headers (X-Agent-Id, X-Valiron-Chain, X-Valiron-Tier, X-Valiron-Route).
History sidebar saves the last 20 requests with one-click replay.
Analytics & Logs
The dashboard provides real-time visibility into your API usage:
- Analytics — Revenue charts (7/30/90 days), top endpoints, today's stats, error rates
- Agents — Every agent that called your API with drill-down into per-agent activity, predictive risk, endpoint breakdown, and call history
- Call Logs — Paginated log of every request with agent, chain, endpoint, status code, cost, and latency
Supported Chains
Valiron is chain-agnostic. Set the default chain at construction or override per-call. All SDK methods accept an optional chain field.
// Set default chain at construction
const valiron = new ValironSDK({ chain: 'monad' });
await valiron.getAgentProfile('8348'); // Uses Monad
// Override per-call (takes precedence)
const valiron2 = new ValironSDK();
await valiron2.checkAgent('8348', { chain: 'base' }); // Base
await valiron2.checkAgent('25459', { chain: 'ethereum' }); // Ethereum
await valiron2.checkAgent('25459'); // Server defaultCore Concepts
Route Decisions
The SDK returns one of four routing decisions:
| Route | Meaning | Trust Tiers |
|---|---|---|
prod | Full access | AAA – BAA |
prod_throttled | Rate-limited access | BA – B |
sandbox | Agent under evaluation | Temporary |
sandbox_only | Access denied | CAA – C |
Wallet Resolution
When an agent pays via x402 or similar protocols, you receive a wallet address — not an agent ID. Valiron resolves wallets to ERC-8004 agent IDs using a multi-layer lookup:
- Redis — Fast cache of previously resolved wallet → agentId mappings
- Agent0 Subgraph — Queries The Graph (Ethereum, Base, Polygon) for on-chain agent registrations by wallet
- Fallback — Returns null if no agent owns the wallet
Wallet → Redis lookup → Agent0 subgraph → agentId (or null)Use resolveWallet() for the lightweight lookup, or getWalletProfile() to get the full trust profile in one call.
Trust Model
Valiron evaluates agents using five independent trust signals. Each signal provides a different dimension of trust — and together they produce the final routing decision. No single signal is required; agents are evaluated based on whatever data is available.
| Signal | What It Measures | Source |
|---|---|---|
| On-Chain Reputation | Feedback from other agents and operators (0-100 per review) | ERC-8004 Reputation Registry |
| Behavioral Sandbox | How the agent handles rate limits, errors, auth, payments, and edge cases in a controlled environment | Valiron Sandbox |
| World ID | Whether the agent is linked to a verified human identity | Worldcoin IDKit |
| Self | Whether the agent is linked to a Self-verified eligible human | Self Enterprise |
| Icebreaker | Whether a human has attested this is their designated agent | Ethereum Attestation Service (Base) |
Selectable Trust Signals
API sellers can use Valiron plug-and-play, or choose which signals score a specific endpoint with trustSignals. Every selected signal is scored on a normalized 0-100 scale, then Valiron normalizes the weights across only the selected signals.
| Signal Key | Plan | Use Case |
|---|---|---|
8004 | Free | On-chain reputation from ERC-8004 or Solana registries |
sandbox | Free | Observed behavioral safety and protocol compliance |
world | Pro / Enterprise | World ID proof-of-personhood link |
self | Pro / Enterprise | Self eligible-human proof and optional Agent ID link |
icebreaker | Pro / Enterprise | Human ownership/background attestation |
app.use('/api/paid', createValironGate({
sdk: valiron,
trustSignals: ['8004', 'sandbox'], // Free
minScore: 70,
}));
app.use('/api/high-trust', createValironGate({
sdk: valiron,
trustSignals: ['8004', 'sandbox', 'world', 'self', 'icebreaker'], // Pro / Enterprise
minScore: 85,
}));On-Chain Reputation (ERC-8004)
The ERC-8004 Reputation Registry stores immutable, on-chain feedback submitted by other agents and operators.
Reputation is stored on-chain at 0x8004BAa17C55a88189AE136b182e5fdA19dE9b63 (same address on all EVM chains).
- Score range: 0-100 per feedback entry
- Self-feedback blocked: Agent owners and approved operators cannot review their own agents
- Tag-based filtering: Feedback can be categorized with two tags (e.g. "reliability", "latency")
- Revocable: The original reviewer can revoke their feedback
- Responses: Any address can append a response to existing feedback
As an API seller, you don't need to interact with this contract directly — Valiron reads it automatically when evaluating agents. But you can view an agent's on-chain reputation in the getAgentProfile() response under onchainReputation.
Behavioral Sandbox
New agents or agents without sufficient on-chain reputation are evaluated in an isolated sandbox environment. The sandbox simulates realistic API interactions and monitors how the agent responds.
The evaluation is holistic — no single factor determines the outcome. The specific factors evaluated, their weights, and the test distribution are proprietary and not disclosed.
See the Sandbox section for graduation rules.
Predictive Risk
Valiron forecasts how likely an agent is to pass its next evaluation or safely handle the next protected action. The model combines recent sandbox scores, score trend, behavioral fingerprints, and production call history into a predicted next score, pass probability, risk direction, confidence, recommended policy, and suggested next tests.
Predictive risk is explainable: each forecast includes human-readable reasons such as worsening score trend, repeated 429 disrespect, elevated error rate, missing backoff behavior, or stable recovery after errors. Low-confidence forecasts mean Valiron has sparse evidence and should usually trigger another sandbox run before broadening production access.
World ID (Proof-of-Personhood)
World ID verifies that an agent is linked to a real human identity via Worldcoin's zero-knowledge proof system. Agents with World ID verification receive a trust boost — the stronger the verification level, the larger the boost.
| Level | Verification | Trust Impact |
|---|---|---|
orb | Biometric (iris scan via World Orb) | Highest boost |
device | Passkey verification | Moderate boost |
phone | SMS verification | Minor boost |
World ID verification is stored immutably — once verified, it cannot be overwritten. Proofs use a nullifier to prevent replay attacks.
As an API seller, you can see whether an incoming agent has World ID verification in the trust profile. You can also encourage your agent customers to verify — it directly improves their trust score on your endpoints.
Self (Eligible-Human Proof)
Self verifies that an agent is linked to a real eligible human through Self Enterprise. Valiron creates a Self verification session, redirects the user to Self, verifies Self's signed webhook, and stores the result as the selectable self trust signal.
Self Agent ID can be included when creating the session. When present and confirmed, Valiron records it as a stronger sub-signal; it is not required for the base eligible-human proof.
const session = await valiron.createSelfSession('25459', {
selfAgentId: 'self-agent-...',
});
console.log(session.self.verificationUrl);Production integrations should use createSelfSession() plus Self's signed /operator/self/webhook delivery. Trusted-payload ingestion is disabled unless SELF_ALLOW_TRUSTED_PAYLOADS=true for local/internal tests.
Icebreaker (Human Attestation)
Icebreaker integration checks whether a human has attested on the Ethereum Attestation Service (EAS) on Base that this agent is their "designated agent."
When present, the agent profile is enriched with the attesting human's identity — their name, verified social handles (X, GitHub, LinkedIn), organization, and Icebreaker profile URL. This provides social context for who is behind an agent.
The Icebreaker attestation appears in the agent profile returned by getAgentProfile().
Plans & Limits
Valiron offers Free and Pro plans. Upgrade via the Operator Dashboard.
| Feature | Free | Pro |
|---|---|---|
| Hosted wrappers / registered endpoints | 3 | Unlimited |
| Sandbox tests / day | 5 | Unlimited |
| Full analytics | — | ✓ |
| Enriched profiles | — | ✓ |
| Webhooks | — | ✓ |
| Proxy gateway | — | ✓ |
| Evaluation history | — | ✓ |
When you hit a plan limit, the API returns 403 with {"error": "upgrade_required", "feature": "...", "message": "This feature requires a Pro plan."}
API Reference
Constructor
new ValironSDK(config?: ValironConfig)| Option | Type | Default | Description |
|---|---|---|---|
apiKey | string | — | API key (reserved for future use) |
endpoint | string | production | Valiron API base URL (override for self-hosted or dev) |
chain | SupportedChain | server default | Default blockchain network |
timeout | number | 5000 | Request timeout (ms) |
debug | boolean | false | Enable debug logging |
telemetry | TelemetryConfig | enabled | Telemetry settings (see Telemetry) |
checkAgent(agentId, options?)
Quick routing check — returns just the route decision string.
const route = await valiron.checkAgent('25459');
// route: 'prod' | 'prod_throttled' | 'sandbox' | 'sandbox_only'
// With chain override
const monadRoute = await valiron.checkAgent('8348', { chain: 'monad' });getAgentProfile(agentId, options?)
Full trust profile combining on-chain identity, reputation, behavioral data, and routing.
const profile = await valiron.getAgentProfile('25459');
// Response includes:
// profile.identity — on-chain identity (wallet, name, endpoints)
// profile.onchainReputation — ERC-8004 feedback (count, averageScore)
// profile.localReputation — behavioral score, tier, riskLevel
// profile.routing — finalRoute, decision, reasons, signalsgetWalletProfile(wallet, options?)
Reverse-lookup a wallet address to get its trust profile.
const profile = await valiron.getWalletProfile('0x52ce...');
console.log(profile.routing.finalRoute);
// With chain override
const monadProfile = await valiron.getWalletProfile('0xABC...', { chain: 'monad' });resolveWallet(wallet, options?)
Lightweight wallet → agentId resolution without fetching 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.
const resolution = await valiron.resolveWallet('0x52ce...');
console.log(resolution.agentId); // '25459' or null
console.log(resolution.source); // 'redis' | 'subgraph' | 'cache' (in-memory) | 'none'
console.log(resolution.agentName); // 'MyAgent' (from subgraph, if available)
console.log(resolution.chainId); // 1 (from subgraph, if available)
// Then fetch the full profile if needed
if (resolution.agentId) {
const profile = await valiron.getAgentProfile(resolution.agentId);
}| Field | Type | Description |
|---|---|---|
wallet | string | The queried wallet address |
agentId | string | null | Resolved ERC-8004 agent ID, or null if not found |
source | ResolutionSource | "redis" | "subgraph" | "cache" | "none" |
chainId | number | null | Chain ID from the subgraph (if available) |
agentName | string | null | Agent name from the subgraph (if available) |
timestamp | string | ISO timestamp of the resolution |
triggerSandboxTest(agentId, options?)
Run real sandbox tests against an agent and compute a Valiron score.
const result = await valiron.triggerSandboxTest('25459');
console.log(result.valironScore); // 95
console.log(result.tier); // 'AAA'
console.log(result.riskLevel); // 'GREEN'
console.log(result.mode); // 'endpoint-probe' | 'sandbox-relay'
console.log(result.testSummary); // opaque metrics objecttriggerKeyAgentSandbox(agentAddress)
Run sandbox tests against a key-based (Web2) agent. Uses sandbox_relay mode (no endpoint probing needed).
const result = await valiron.triggerKeyAgentSandbox('0x1234...abcd');
console.log(result.tier); // 'A'
console.log(result.riskLevel); // 'GREEN'
console.log(result.valironScore); // 85totalFeedback === 0 trigger triggerSandboxTest(), and key agents with no score trigger triggerKeyAgentSandbox(). The agent receives a 403 with { error: "Agent pending evaluation", retryAfterMs: 30000 }.gate(agentId, options?)
Trust gate — single call combining on-chain reputation + sandbox tests. Designed to sit in front of payment protocols (x402 etc.). Returns an allow/deny decision with a combined score.
const result = await valiron.gate('25459');
console.log(result.allow); // true
console.log(result.score); // 95
console.log(result.tier); // 'AAA'
console.log(result.riskLevel); // 'GREEN'
console.log(result.sandboxRan); // true (first call) / false (cached)
// Custom threshold
const result2 = await valiron.gate('8348', {
minScore: 85, // require A tier or above
trustSignals: ['8004', 'sandbox'],
ttlMs: 3600_000, // re-test every hour
chain: 'monad', // chain override
});
if (result2.allow) {
// proceed to payment
} else {
console.log(result2.reasons); // ['Score 42 below threshold 85']
}| Option | Type | Default | Description |
|---|---|---|---|
minScore | number | 65 | Minimum score to allow (0–100) |
trustSignals | Array<'8004' | 'sandbox' | 'world' | 'self' | 'icebreaker'> | auto | Optional selected scoring signals. Free supports 8004 and sandbox; Pro adds world, self, and icebreaker. |
ttlMs | number | 86400000 | Cache TTL for sandbox results (ms) |
chain | SupportedChain | constructor default | Blockchain network override |
checkAgent() returns a route decision based on existing data. gate() actively runs sandbox tests (if stale/missing), computes a combined score, and returns a binary allow/deny — ideal for protecting payment endpoints.Provisional scores: If result.provisional === true, the score is from a fast micro-sandbox. A full deep test is running in the background and will update the cached score automatically. Provisional scores are conservative — they may under-estimate the agent's real trust level.
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.
const snapshot = await valiron.getAgentSnapshot('25459');
console.log(snapshot.snapshotHash); // '0x3a7f...' (opaque hash)
console.log(snapshot.previousHash); // '0x0' (genesis) or '0x8b2c...'
console.log(snapshot.encryptedDataUri); // IPFS CID or null
console.log(snapshot.interactionCount); // 15The 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".
Webhooks
Register a webhook to receive evaluation_complete events whenever Valiron evaluates an agent. The payload matches the getAgentSnapshot() response. Pro plan required.
curl -X POST https://valiron-edge-proxy.onrender.com/operator/webhooks/register \
-H "Content-Type: application/json" \
-d '{"event": "evaluation_complete", "url": "https://your-endpoint.com/hooks/valiron", "agentIds": [42, 43]}'The agentIds filter is optional — omit it to receive events for all agents. Duplicate registrations (same event + URL + agentIds) are deduplicated automatically.
| Payload Field | Type | Description |
|---|---|---|
event | string | "evaluation_complete" |
agentId | string | The evaluated agent's ID |
snapshotHash | string | Opaque SHA-256 hash of the evaluation |
previousHash | string | Previous hash in the chain ("0x0" for first) |
interactionCount | number | Total interactions in the evaluation window |
timestamp | string | ISO 8601 timestamp |
proxy(agentId, request, options?)
Proxy an API call through Valiron's trust gateway. The agent sends a request to Valiron, Valiron evaluates trust inline, and either forwards to your real API or blocks with 403. The agent never touches your API directly. Pro plan required.
const result = await valiron.proxy('25459', {
target: 'https://api.example.com/data',
method: 'GET',
headers: { 'Authorization': 'Bearer ...' },
minScore: 65, // minimum trust score (default: 65)
});
// result._valiron — gate evaluation (allow, score, tier, riskLevel)
// result._apiStatus — HTTP status from your real API
// result.response — your API's response body
if (result._valiron.allow) {
console.log(result.response); // Real API data
}| ProxyRequest Field | Type | Description |
|---|---|---|
target | string | Your real API endpoint URL (must be HTTPS, public IP) |
method | string | HTTP method (default: GET) |
headers | Record<string, string> | Headers to forward to target API |
body | unknown | Request body for POST/PUT/PATCH |
minScore | number | Minimum trust score to allow (default: 65) |
Key-Based Agents (Web2)
Not every agent has an ERC-8004 identity. Local, Web2, and non-ERC-8004 agents use a persistent Ethereum-style keypair for identity without needing an on-chain registration. Identity is verified via EIP-191 challenge-response.
| Agent type | Header | Lookup path |
|---|---|---|
| ERC-8004 / Solana | x-agent-id | /operator/agent/:agentId |
| Local / Web2 / non-ERC-8004 | x-agent-address | /operator/key/:agentAddress |
my-agent to /operator/agent/:agentId. That path is for registry-backed on-chain agents. Local agents should generate or reuse a persistent keypair, complete auth.md, and use their verified x-agent-address.// 1. Get a challenge nonce
const { challenge, expiresAt } = await valiron.getKeyAgentChallenge('0x1234...abcd');
// 2. Agent signs the challenge with its private key
const signature = await account.signMessage({ message: challenge });
// 3. Verify — returns the agent's trust profile
const profile = await valiron.verifyKeyAgent({
challenge,
signature,
agentAddress: '0x1234...abcd',
});
console.log(profile.verified); // true
console.log(profile.score); // 85
console.log(profile.tier); // 'A'
console.log(profile.icebreaker); // Icebreaker enrichment (if attested)
// 4. Subsequent lookups (no challenge needed)
const profile2 = await valiron.getKeyAgentProfile('0x1234...abcd');Middleware auto-detection: All middleware (createValironGate, paywall, etc.) automatically checks x-agent-id first (on-chain), then falls back to x-agent-address (key-based). No configuration needed — both agent types work through the same middleware.
Auto-sandbox for key agents: New key agents (verified but not yet scored) are automatically sandbox-evaluated on their first gated request. Use triggerKeyAgentSandbox() or POST /operator/trigger-sandbox-key/{address} to trigger manually.
World ID SDK Methods
Programmatic World ID verification and status lookup. See World ID in Trust Model for background.
// Verify a World ID proof (from IDKit)
const result = await valiron.verifyWorldId('25459', {
proof: '0x...',
merkle_root: '0x...',
nullifier_hash: '0x...',
verification_level: 'orb', // 'orb' | 'device' | 'phone'
});
console.log(result.worldId.verified); // true
// Check verification status
const status = await valiron.getWorldIdStatus('25459');
console.log(status.worldId.verificationLevel); // 'orb'
// Enriched profile (World ID + Icebreaker combined)
const profile = await valiron.getWorldIdProfile('25459');
console.log(profile.humanTrust.hasHumanLink); // true
console.log(profile.humanTrust.hasBackground); // true
console.log(profile.icebreaker?.verifiedHandles);
// [{ source: 'github', handle: 'alice' }, { source: 'x', handle: '@alice' }]Self SDK Methods
Programmatic Self Enterprise session creation, status lookup, and profile retrieval. See Self in Trust Model for background.
// Create a Self Enterprise verification session
const session = await valiron.createSelfSession('25459', {
selfAgentId: 'self-agent-...',
successUrl: 'https://example.com/verified',
failureUrl: 'https://example.com/failed',
});
console.log(session.self.verificationUrl);
// Check Self status
const status = await valiron.getSelfStatus('25459');
console.log(status.self.verified);
// Full Self eligible-human / Agent ID profile
const profile = await valiron.getSelfProfile('25459');
console.log(profile.humanTrust.hasHumanLink);dispose()
Flush pending telemetry and release resources. Call this in long-running servers (Express, Fastify) on graceful shutdown. Optional for short-lived scripts — the SDK flushes automatically on process exit.
const valiron = new ValironSDK();
try {
const profile = await valiron.getAgentProfile('25459');
} finally {
await valiron.dispose();
}
// Or on server shutdown
process.on('SIGTERM', async () => {
await operator.dispose();
process.exit(0);
});Middleware
Drop-in middleware for every major Node.js framework. All middleware variants auto-detect on-chain (x-agent-id) and key-based (x-agent-address) agents automatically.
Express / Connect
import { ValironSDK, createValironGate } from '@valiron/sdk';
const valiron = new ValironSDK({ chain: 'monad' });
// Basic — checks x-agent-id first, then x-agent-address, minScore 65
// Includes a 30s client-side cache for payment retries and cold starts
// Safe with Express 4 (async errors handled via Promise.catch(next))
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 + hooks
app.use('/api/custom', createValironGate({
sdk: valiron,
extractAgentId: (req) => req.query.agent_id as string,
onAllow: (req, result) => {
console.log(`Allowed agent ${result.agentId} (score: ${result.score})`);
},
onDeny: (req, res, result) => {
res.status(403).json({
error: 'Insufficient trust score',
requiredTier: 'A',
currentTier: result.tier,
});
},
}));Fastify
import Fastify from 'fastify';
import { ValironSDK, valironFastifyPlugin } from '@valiron/sdk';
const app = Fastify();
const valiron = new ValironSDK({ chain: 'monad' });
// Gate all routes
app.register(valironFastifyPlugin, { sdk: valiron });
// Or gate a specific prefix
app.register(valironFastifyPlugin, {
sdk: valiron,
prefix: '/api',
minScore: 85,
});
app.get('/api/data', async (req) => {
// req.valiron is populated with GateResult
return { data: 'protected', tier: req.valiron.tier };
});Next.js
Use createValironNextMiddleware() in your project's middleware.ts to gate API routes.
// middleware.ts (Next.js project root)
import { ValironSDK, createValironNextMiddleware } from '@valiron/sdk';
const valiron = new ValironSDK({ chain: 'monad' });
const valironMiddleware = createValironNextMiddleware({
sdk: valiron,
paths: ['/api/paid', '/api/premium'], // only gate these paths
minScore: 65,
});
export async function middleware(request: Request) {
return valironMiddleware(request);
}
export const config = { matcher: '/api/:path*' };Returns undefined (pass through) when the agent is allowed, or a JSON Response (401/403/503) when blocked. Paths not in the paths array are passed through without checking.
Generic (Hono, Koa, Bun…)
Use valironGateCheck() for any framework. Returns a structured result you can map to any HTTP response type.
import { ValironSDK, valironGateCheck } from '@valiron/sdk';
const valiron = new ValironSDK();
// Hono
app.use('/api/*', async (c, next) => {
const gate = await valironGateCheck(valiron, {
agentId: c.req.header('x-agent-id') ?? null,
agentAddress: c.req.header('x-agent-address') ?? null,
minScore: 65,
});
if (!gate.allow) return c.json(gate.body, gate.status);
c.set('valiron', gate.result);
await next();
});
// Koa
app.use(async (ctx, next) => {
const gate = await valironGateCheck(valiron, {
agentId: ctx.get('x-agent-id') || null,
agentAddress: ctx.get('x-agent-address') || null,
});
if (!gate.allow) {
ctx.status = gate.status;
ctx.body = gate.body;
return;
}
ctx.state.valiron = gate.result;
await next();
});Middleware Error Responses
All middleware variants use identical error responses:
| Status | When | Body |
|---|---|---|
| 401 | No x-agent-id or x-agent-address header | { error, message, identitySkill } |
| 403 | Agent blocked (score below threshold) | { error, allow, score, tier, riskLevel, route, ... } |
| 403 | New agent pending evaluation | { error: "Agent pending evaluation", retryAfterMs: 30000 } |
| 503 | Gate unavailable (network error, timeout) | { error: "Trust gate unavailable — request denied (fail-closed)" } |
503 and denies the request. Valiron never fails open — an unknown agent is never allowed through.Operator SDK
The ValironOperator class is purpose-built for API sellers who want to monetize their endpoints. It wraps the trust gate with pricing and usage tracking — so you can protect and charge for your API with one line of middleware.
Requires an operator API key (val_op_ prefix) from the Operator Dashboard.
import { ValironOperator } from '@valiron/sdk';
const operator = new ValironOperator({
apiKey: 'val_op_xxxx', // from valiron.co/dashboard
chain: 'ethereum', // default chain (optional)
timeout: 5000, // ms (optional)
debug: false, // log gate checks (optional)
});| Option | Type | Required | Description |
|---|---|---|---|
apiKey | string | Yes | Operator API key (must start with val_op_) |
chain | SupportedChain | No | Default blockchain (default: ethereum) |
timeout | number | No | Request timeout in ms (default: 5000) |
debug | boolean | No | Log gate checks to console (default: false) |
paywall(config)
Express/Connect middleware that gates access and tracks usage. Extracts X-Agent-Id from request headers, runs the trust gate, and attaches req.valiron with the gate result and pricing info.
// One line to monetize an endpoint
app.use('/api/inference', operator.paywall({
pricePerCall: 0.05, // USD per call
minTrustScore: 45, // minimum score to allow (default: 45)
ttlMs: 86400000, // cache TTL in ms (default: 24h)
}));
app.post('/api/inference', (req, res) => {
// req.valiron.allow === true (guaranteed — denied agents never reach here)
// req.valiron.tier === 'AAA'
// req.valiron.pricePerCall === 0.05
res.json({ result: 'inference output' });
});| Option | Type | Default | Description |
|---|---|---|---|
pricePerCall | number | — | Price per API call in USD (required) |
minTrustScore | number | 45 | Minimum Valiron score to allow access |
ttlMs | number | 86400000 | Cache TTL for trust results (ms, default 24h) |
onDeny | (req, res, result) => void | 403 JSON | Custom handler when agent is denied |
onAllow | (req, result) => void | — | Hook called after agent is allowed |
With custom hooks:
app.use('/api/premium', operator.paywall({
pricePerCall: 0.10,
minTrustScore: 65,
onDeny: (req, res, result) => {
res.status(403).json({
error: 'Agent blocked',
tier: result.tier,
score: result.score,
message: 'Minimum trust score of 65 required. Improve your on-chain reputation or pass sandbox evaluation.',
});
},
onAllow: (req, result) => {
console.log(`Agent ${result.agentId} allowed — tier: ${result.tier}, price: $0.10`);
},
}));fastifyPaywall(config)
Same as paywall() but for Fastify. Register as a plugin with an optional route prefix.
import Fastify from 'fastify';
import { ValironOperator } from '@valiron/sdk';
const app = Fastify();
const operator = new ValironOperator({ apiKey: 'val_op_xxxx' });
// Gate all routes under /api
app.register(operator.fastifyPaywall({
pricePerCall: 0.10,
prefix: '/api',
minTrustScore: 65,
}));
app.get('/api/data', async (req, reply) => {
// req.valiron is populated with gate result + pricing
return { data: 'protected content', tier: req.valiron.tier };
});Accepts the same options as paywall() plus an optional prefix to scope the gate to specific routes.
createValironGate() is a low-level middleware that only gates access. ValironOperator.paywall() adds pricing, operator identity, and usage tracking on top — it's the recommended integration for API sellers.Dynamic Scoring
Valiron enables trust-based dynamic pricing for your API endpoints. Agents with lower trust scores pay more per request — covering the potential infrastructure damage they may cause — while highly trusted agents get discounted rates as a reward for good behavior.
This creates a self-balancing economic model: bad actors subsidize their own risk, and good actors are incentivized to maintain their reputation.
Pricing by Trust Tier
| Tier | Trust Level | Risk | Relative Price |
|---|---|---|---|
| AAA | Highest | Prime | Lowest |
| AA | Very high | High grade | Low |
| A | High | Upper medium | Low |
| BAA | Medium | Medium | Medium |
| BA | Below medium | Speculative | Higher |
| B | Low | Highly speculative | High |
| CAA – C | Lowest | Substantial risk | Blocked |
Implementation
Use the trust gate result to set per-request pricing. This example pairs Valiron with x402 payment headers:
import { ValironSDK, createValironGate } from '@valiron/sdk';
const valiron = new ValironSDK({ chain: 'monad' });
const trustGate = createValironGate({ sdk: valiron, minScore: 50 });
// Pricing table — bad agents pay more to offset infra risk
const PRICE_BY_TIER: Record<string, number> = {
AAA: 5, // $0.005
AA: 5, // $0.005
A: 8, // $0.008
BAA: 10, // $0.010
BA: 25, // $0.025
B: 50, // $0.050
};
function dynamicPricing(req: any, res: any, next: any) {
const tier = req.valiron?.tier || 'B';
const price = PRICE_BY_TIER[tier] ?? 50;
// Attach price to request for downstream payment check
req.priceMillicents = price;
// If payment header is missing, return 402 with tier-adjusted price
if (!req.headers['x-payment']) {
return res.status(402).json({
accepts: [{
scheme: 'exact',
maxAmountRequired: price,
payTo: '0x...',
network: 'monad',
}],
meta: { tier, score: req.valiron?.score },
});
}
next();
}
// Flow: Trust Gate → Dynamic Pricing → Handler
app.get('/api/data', trustGate, dynamicPricing, (req, res) => {
res.json({ data: 'Here you go', tier: req.valiron.tier });
});Sandbox
The sandbox is an isolated testing environment where Valiron evaluates agent behavior before granting production access to your API. New agents start in the sandbox automatically.
- A sandbox test is triggered (via SDK, HTTP API, or automatically for new agents)
- The agent receives a series of simulated API interactions
- The sandbox monitors how the agent handles normal requests, rate limits, errors, and edge cases
- A trust evaluation is performed and a Moody's-style tier is assigned
- Based on the tier, a route decision determines what access level the agent receives
The sandbox tests against a mix of successful responses, rate-limit responses (429), server errors (500), auth challenges (401/403), and payment requests (402). The exact distribution and test patterns are proprietary.
Graduation
Agents graduate from sandbox to production based on their behavioral evaluation:
| Outcome | Route | What It Means |
|---|---|---|
| Graduated | prod | Strong behavioral compliance — full production access |
| Throttled | prod_throttled | Passes with concerns — allowed with rate limiting |
| Blocked | sandbox_only | Risky behavior — blocked from production |
Once graduated, the trust evaluation is cached. The route decision persists until a new sandbox test is triggered or the cache expires (default 24 hours).
Tips for Passing
Agents that demonstrate responsible, well-behaved API usage will earn higher trust tiers. The specific scoring criteria are proprietary, but in general: respect API boundaries, handle errors gracefully, and avoid aggressive request patterns.
Auto-Sandbox
When a new agent hits a gated endpoint for the first time, middleware automatically triggers sandbox evaluation in the background. No manual trigger is needed.
| Agent Type | Detection | Trigger |
|---|---|---|
| On-chain (ERC-8004) | totalFeedback === 0 | triggerSandboxTest() |
| Key-based (Web2) | verified && score === null | triggerKeyAgentSandbox() |
While evaluation runs, the agent receives a 403 with { error: "Agent pending evaluation", retryAfterMs: 30000 }. The cache prevents duplicate triggers during the evaluation window.
Solana
Solana agents use the QuantuLabs 8004-solana registry instead of EVM contracts. The SDK handles the difference automatically — just pass chain: 'solana'.
| Concept | EVM | Solana |
|---|---|---|
| Agent ID | Numeric token ID (e.g. 25459) | Base-58 Metaplex pubkey or sequential integer |
| Wallet format | 0x-prefixed Ethereum address | Base-58 Solana public key |
| Identity Registry | 0x8004A169... | 8oo4dC4Jv... |
| Reputation | 0x8004BAa1... | ATOM engine (AToMw53ai...) |
| Feedback write-back | Manual | Automatic after sandbox evaluation |
const valiron = new ValironSDK({ chain: 'solana' });
// By asset pubkey
const profile = await valiron.getAgentProfile('DFkntsJ9aTCHkK88m6WZVQEvLdLNi4X1LVUgRMqetFPM');
// By sequential ID (convenience)
const profile2 = await valiron.getAgentProfile('1226');
// Liveness check — pings all declared service endpoints
const liveness = await fetch(
'https://valiron-edge-proxy.onrender.com/operator/agent/DFkntsJ9.../liveness?chain=solana'
);ATOM engine metrics: Solana reputation includes additional fields not available on EVM — trustTier, qualityScore, confidence, riskScore, and uniqueCallers. These appear in the profile under onchainReputation.solana.
Automatic feedback write-back: After every sandbox test or gate evaluation, Valiron writes the computed score back to the Solana reputation registry via the ATOM engine's giveFeedback(). This means Solana agents build on-chain reputation automatically through normal Valiron usage.
Response Types
All types are exported from @valiron/sdk and can be imported directly for type-safe code.
import type {
AgentProfile, WalletProfile, GateResult, SandboxResult,
AgentSnapshotSummary, WalletResolution, KeyAgentProfile,
WorldIdStatus, WorldIdAgentProfile, ProxyResponse,
RouteDecision, MoodysRating, RiskLevel, SupportedChain,
ValironError,
} from '@valiron/sdk';GateResult
| Field | Type | Description |
|---|---|---|
allow | boolean | Whether the agent is allowed through |
score | number | Combined Valiron trust score (0–100) |
tier | MoodysRating | AAA through C |
riskLevel | RiskLevel | "GREEN" | "YELLOW" | "RED" |
route | RouteDecision | prod / prod_throttled / sandbox / sandbox_only |
agentId | string | The evaluated agent's ID |
wallet | string | Agent's wallet address |
chain | ChainContext | Network, registry addresses used |
sandboxRan | boolean | Whether sandbox tests ran this call (false = cached) |
cached | boolean | Whether cached results were used |
provisional | boolean? | If true, score is from a fast micro-sandbox; a full deep test is running in the background |
AgentProfile
| Field | Type | Description |
|---|---|---|
agentId | string | ERC-8004 agent ID |
identity | AgentIdentity | On-chain identity (wallet, name, endpoints, trust models) |
onchainReputation | OnchainReputation | Feedback count, average score, entries, and Solana ATOM data |
localReputation | LocalReputation | null | Behavioral score, tier, riskLevel, graduated status |
routing | RoutingExplanation | finalRoute, decision text, reasons array, signals breakdown |
chain | ChainContext | Network, RPC URL, registry contract addresses |
worldId | WorldIdStatus? | World ID verification status (if checked) |
timestamp | string | ISO 8601 timestamp |
KeyAgentProfile
| Field | Type | Description |
|---|---|---|
agentAddress | string | Ethereum address (keccak256 of secp256k1 pubkey) |
verified | boolean | Identity verified via challenge-response |
score | number | null | Behavioral score (0–100) |
tier | MoodysRating | null | AAA through C |
riskLevel | RiskLevel | null | GREEN / YELLOW / RED |
route | RouteDecision | null | Route decision |
icebreaker | IcebreakerOwnership | null | Icebreaker attestation context |
reasons | string[] | Human-readable trust explanation |
Request Headers
| Header | Purpose | Notes |
|---|---|---|
X-Agent-Id | On-chain agent ID (ERC-8004 / Solana) | Checked first by all middleware |
X-Agent-Address | Key-based agent Ethereum address | Fallback when no X-Agent-Id |
X-Chain | Blockchain network override | Alternative to ?chain= query param |
Authorization | Operator API key | Bearer val_op_xxxx |
All Exports
Everything available from @valiron/sdk:
// Classes
export { ValironSDK, ValironOperator, ValironClient, ValironError };
// Middleware
export { createValironGate, valironFastifyPlugin, createValironNextMiddleware, valironGateCheck };
// Utilities
export { isValidAddress, generateChallenge };
// All TypeScript types (AgentProfile, GateResult, etc.)
export type { ... }; // See full list in types.tsTelemetry
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 authenticated with the operator API key.
// Disable telemetry entirely
const valiron = new ValironSDK({
telemetry: { enabled: false },
});
// Reduce volume with sampling
const valiron2 = new ValironSDK({
telemetry: { sampleRate: 0.1 }, // only 10% of events
});
// Custom event handler (e.g. forward to your own analytics)
const valiron3 = new ValironSDK({
telemetry: {
onEvent: (event) => myAnalytics.track(event),
},
});| Option | Type | Default | Description |
|---|---|---|---|
enabled | boolean | true | Enable/disable telemetry |
sampleRate | number | 1.0 | Sampling rate (0.0–1.0) |
appId | string | — | Application identifier for grouping |
env | "prod" | "staging" | "dev" | "prod" | Environment label |
onEvent | (event) => void | — | Custom event callback |
Error Handling
All SDK methods throw typed ValironError instances with a code field for programmatic handling.
import { ValironError } from '@valiron/sdk';
try {
const profile = await valiron.getAgentProfile('123');
} catch (error) {
if (error instanceof ValironError) {
switch (error.code) {
case 'TIMEOUT_ERROR': // Request timed out
case 'NETWORK_ERROR': // Could not reach API
case 'API_ERROR': // Non-2xx response (check error.statusCode)
}
}
}| Error Code | Meaning |
|---|---|
TIMEOUT_ERROR | Request exceeded the configured timeout (default 5000ms) |
NETWORK_ERROR | Could not reach the Valiron API (DNS, connection refused, etc.) |
API_ERROR | Server returned a non-2xx response — check error.statusCode |
Common HTTP status codes from the Valiron API:
| Status | Cause |
|---|---|
400 | Invalid agentId format or missing required fields |
401 | Missing agent identity header (middleware) or invalid operator API key |
403 | Agent blocked (score below threshold) or feature requires Pro plan |
404 | Agent not found on the specified chain |
429 | Sandbox daily limit exceeded (free plan) |
503 | Trust gate unavailable — request denied (fail-closed) |
API Scaffolding
API Scaffolding is a separate Valiron product for exposing existing HTTPS APIs through hosted, agent-facing URLs. It supports rapid distribution and evaluation while providing a migration path to first-party backend deployments protected by Valiron middleware.
Agent -> /wrap/{operatorId}/{path}
-> x402 or MPP payment challenge, if price > 0
-> optional Valiron trust gate
-> rate limit + SSRF protection
-> upstream API requestDashboard Setup
- Open valiron.co/dashboard.
- Go to Make API Agent-Ready.
- Enter the public path, HTTP method, upstream API base URL, price, payment protocol, payment network, and payout wallet.
- Enable trust checks when the wrapper should require agent identity and trust evaluation before forwarding upstream.
- Configure upstream auth if your existing API needs a bearer token, API key header, or HMAC signing secret.
- Distribute the generated wrapper URL to agent clients.
| Field | Description |
|---|---|
path | Public wrapper path, for example /weather. |
targetUrl | Existing upstream HTTPS base URL. Valiron appends path when forwarding. |
pricePerCall | Use 0 for free wrappers, or a positive USD amount for paid calls. |
paymentProtocol | x402 or mpp. |
paymentNetwork | Use eip155:8453 or eip155:84532 for x402; tempo:4217 or tempo:42431 for MPP. |
trustEnabled | When true, Valiron requires agent identity headers and checks trust before forwarding. |
HTTP API Setup
The dashboard uses the same JWT-authenticated endpoint you can call directly:
curl -X POST https://valiron-edge-proxy.onrender.com/operator/endpoints \
-H "authorization: Bearer $VALIRON_JWT" \
-H "content-type: application/json" \
-d '{
"path": "/weather",
"method": "GET",
"pricePerCall": 0.001,
"targetUrl": "https://api.example.com",
"paymentProtocol": "x402",
"paymentNetwork": "eip155:8453",
"payoutAddress": "0xYourPayoutWallet",
"trustEnabled": false,
"upstreamAuthType": "none"
}'Calling Wrappers
Agent clients call the generated public wrapper URL. Free wrappers forward immediately. Paid wrappers return a 402 Payment Required challenge and require the caller to retry with payment.
curl -i https://valiron-edge-proxy.onrender.com/wrap/{operatorId}/weatherx402 & MPP
x402 wrappers use Base-compatible HTTP payments. An unpaid request returns a 402 challenge, and the paid retry includes an X-PAYMENT header. MPP wrappers use WWW-Authenticate: Payment and retry with Authorization: Payment ....
pnpm exec mppx account create
pnpm exec mppx https://valiron-edge-proxy.onrender.com/wrap/{operatorId}/researchDeploy With Middleware Protection
For production deployments that require full backend control, implement Valiron middleware directly in the service using createValironGate() or ValironOperator.paywall().
import { ValironSDK, createValironGate } from '@valiron/sdk';
const valiron = new ValironSDK();
app.use('/api/weather', createValironGate({
sdk: valiron,
minScore: 70,
}));