@abstraxn/warrant
v0.1.0
Published
KYI Warrant server SDK — createMandate() and check() before agent actions (ALLOW / DENY / ESCALATE)
Readme
@abstraxn/warrant
Server / Node SDK for KYI Framework → Warrant — create sealed mandates and gate agent actions with ALLOW / DENY / ESCALATE before execution.
Related packages:
| Package | Role |
|---------|------|
| @abstraxn/warrant | Server / Node client (createMandate, check) |
| @abstraxn/warrant-react | MandateForm + deterministic Readback |
| @abstraxn/warrant-verifier | Offline receipt verify + CLI |
Installation
npm install @abstraxn/warrantyarn add @abstraxn/warrantpnpm add @abstraxn/warrantRequires Node.js 18+ (global fetch).
Prerequisites
- Node.js >= 18 (global
fetch) - Application API key from the Abstraxn Dashboard (create / manage mandates)
- Warrant API URL — optional; defaults to
https://api-warrant.abstraxn.com
API keys (per-mandate, like Agent Kit per-agent)
| Key | Who | Use |
|-----|-----|-----|
| Application API key | Business / dashboard | Create & manage mandates |
| Mandate API key | Runtime agent / MCP / Kong | check() under that mandate only |
On createMandate, KYI mints a Kong key and returns it once as result.apiKey. Store it securely — runtime agents use that key, not the business app key.
Basic integration
1. Initialize (admin — application key)
import { Warrant } from '@abstraxn/warrant';
const admin = new Warrant({
apiKey: process.env.ABSTRAXN_API_KEY!, // application API key
// apiUrl optional — defaults to https://api-warrant.abstraxn.com
});2. Create a mandate
import {
Warrant,
hashMandateRules,
mandateSealMessage,
} from '@abstraxn/warrant';
const rules = {
rules: [
{ type: 'amount_max_per_action', value: 100, currency: 'USD' },
{
type: 'counterparty_allowlist',
value: ['0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'],
},
],
};
const hash = hashMandateRules(rules); // JCS + SHA-256 — must match KYI
const message = mandateSealMessage(hash);
// wallet: eth_personalSign(message) → owner_signature
// owner_pubkey_ref: `eip155:<chainId>:${address.toLowerCase()}`
const mandate = (await admin.createMandate({
agent_id: 'agent_web3_demo',
principal_id: '0xowner…',
domain: 'web3',
rules,
owner_signature: '0x…', // EIP-191 or Ed25519 seal
owner_pubkey_ref: 'eip155:80002:0xowner…',
valid_until: null,
})) as { id: string; apiKey: string; status: string };
// Store mandate.apiKey once — returned only on create.
console.log('Mandate ID:', mandate.id);
console.log('Mandate API Key:', mandate.apiKey);KYI verifies EIP-191 (eip155:…) and Ed25519 (ed25519:<pubkeyHex>) seals on create. Passkey (webauthn:…) needs WARRANT_SEAL_ALLOW_UNVERIFIED=webauthn until full assertion verify ships.
3. Runtime check (mandate key)
const warrant = new Warrant({
apiKey: process.env.WARRANT_MANDATE_API_KEY!, // per-mandate key
});
const decision = await warrant.check({
agent_id: 'agent_web3_demo', // must match mandate; forced from key when using mandate key
domain: 'web3',
action_type: 'transfer',
value: { amount: 40, currency: 'USD' },
counterparty: {
id: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
type: 'evm_address',
},
});
if (decision.verdict !== 'ALLOW') {
throw new Error(`Blocked: ${decision.reasons.map((r) => r.code).join(', ')}`);
}
// proceed with the real transfer / MCP toolConfiguration
| Option | Required | Default | Description |
|-----------|----------|---------|-------------|
| apiUrl | No | https://api-warrant.abstraxn.com (or WARRANT_URL / KYI_URL env) | KYI Warrant base URL |
| apiKey | Yes | — | Application or mandate API key (x-api-key) |
| onError | No | 'deny' | Behavior when Warrant is unreachable |
onError behavior
| Value | When API / network fails |
|-------|--------------------------|
| 'deny' | Returns a synthetic DENY decision (WARRANT_UNREACHABLE) |
| 'escalate' | Returns a synthetic ESCALATE decision |
| 'bypass_with_receipt' | Re-throws the error (caller handles) |
API reference
createMandate(params)
Creates a sealed mandate. Requires the application API key.
type CreateMandateParams = {
agent_id: string;
principal_id: string;
domain: string;
rules: RuleSet;
owner_signature: string;
owner_pubkey_ref: string;
valid_until?: string | null;
};Seal helpers
| Export | Description |
|--------|-------------|
| hashMandateRules(rules) | JCS + SHA-256 hex (same as KYI mandate.hash) |
| canonicalRulesJson(rules) | JCS string of rules |
| mandateSealMessage(hash) | EIP-191 / Ed25519 message to sign |
check(action)
Evaluates an action against active mandates. Prefer the mandate API key at runtime.
type NormalizedAction = {
agent_id: string;
domain: string;
action_type: string;
value: { amount: number; currency: string };
counterparty?: { id: string; type: string } | null;
items?: Array<{ name: string; category?: string }> | null;
timestamp?: string; // default: now (ISO)
};
type Decision = {
decision_id: string;
verdict: 'ALLOW' | 'DENY' | 'ESCALATE';
reasons: Array<{ code: string; layer: string; detail?: unknown }>;
matched_mandate_ids: string[];
inputs_digest: string;
receipt_id: string;
evaluated_at: string;
};Rule types
type Rule =
| { type: 'amount_max_per_action'; value: number; currency: string }
| { type: 'category_denylist'; value: string[] }
| { type: 'counterparty_allowlist'; value: string[] }
| {
type: 'time_window';
value: { days: string[]; from: string; to: string; tz: string };
};Environment variables (typical)
# Optional — omit apiUrl in code to use production default
# WARRANT_URL=https://api-warrant.abstraxn.com
ABSTRAXN_API_KEY=<application api key>
WARRANT_MANDATE_API_KEY=<per-mandate key from createMandate>
WARRANT_AGENT_ID=agent_web3_demoExamples
abstraxn-agent-examples/examples/09-warrant-gated-transfer— Next app wrapping MCPtransferwithwarrant.check()kyi-pocs/— passkey, wallet, and web3 CLI POCs
Security notes
- Treat mandate
apiKeylike an agent secret — encrypt at rest; never ship in public frontend bundles. - Always call
check()before irreversible actions (transfers, orders, MCP commit tools). - Fail closed: default
onError: 'deny'when Warrant is unreachable.
License
MIT
