Package Exports
- triage-ows
- triage-ows/dist/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (triage-ows) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
Triage x OWS
Reputation-gated spending policies for the agent economy.
Triage is a scoring server and policy engine for the Open Wallet Standard. It evaluates every transaction an AI agent attempts, assigns a trust score based on 5 factors, and enforces dynamic spending limits. Agents start restricted and earn financial autonomy over time.
Triage does not create agents. It governs how much financial autonomy they earn.
OWS Wallet --> triage-policy (stdin/stdout) --> Scoring Server --> APPROVE / DENY
|
Dashboard + XMTPQuick Start
# 1. Install
npm install triage-ows hono
# 2. Initialize config
npx triage-ows init
# 3. Start the server + dashboard
npx triage-ows devDashboard at http://localhost:4021. WebSocket at ws://localhost:4021/ws.
How It Works
stdin (PolicyContext JSON)
|
+------v------+
| triage- | OWS spawns this as a subprocess
| policy | for every transaction
+------+------+
|
HTTP POST /api/policy/evaluate
|
+------v------+
| Scoring | 5-factor trust formula
| Server | + risk penalty
+------+------+
|
+------------+------------+
| | |
Record in Emit WS XMTP notify
store event (on deny)
| | |
v v v
Agent DB Dashboard Human owner
(in-mem) (React) (can override)- OWS spawns
triage-policyas a subprocess for each transaction - The executable reads
PolicyContextfrom stdin and POSTs to the scoring server - The server computes the agent's trust score across 5 factors
- Three checks run in order: frozen tier, per-transaction limit, daily limit
- Result (
allow: true/false) is written to stdout for OWS - Every decision is broadcast via WebSocket to the dashboard
- Denials trigger XMTP notifications to the human owner, who can reply "override"
Trust Formula
Trust Score = Identity + OnChain + Behavior + Compliance + Network - Risk
(0-35) (0-20) (0-20) (0-15) (0-5) (0-30)
Clamped to [0, 100]Identity (0-35)
Additive scoring with a hard cap at 35:
| Condition | Score | Cumulative |
|---|---|---|
| Fresh wallet (base) | 4 | 4 |
| Has transaction history | 12 | 12 |
| OWS wallet with approved transactions | 20 | 20 |
| + Web Bot Auth verified (RFC 9421) | +4 | 24 |
| + World ID verified (human proof) | +11 | 31-35 |
An OWS wallet with both Web Bot Auth and World ID: min(35, 20 + 4 + 11) = 35.
On-Chain (0-20)
| Sub-factor | Max | Calculation |
|---|---|---|
| Account age | 5 | min(5, monthsOld * 0.5) |
| Transaction count | 5 | min(5, log10(totalRequests) * 2.5) |
| Counterparty diversity | 5 | min(5, uniqueCounterparties / 10 * 5) |
| Balance (funded) | 5 | 5 if on-chain balance > 0, else 0 |
Balance is checked via Base Sepolia RPC (viem), cached for 10 minutes.
Behavior (0-20)
| Sub-factor | Max | Calculation |
|---|---|---|
| Success rate | 5 | min(5, successfulRequests / totalRequests * 5) |
| Pacing | 5 | 5 if <5 req/min, 2 if 5-15, 0 if >15 |
| Clean days | 5 | min(5, consecutiveCleanDays * 0.5) |
| Counterparty concentration | 5 | 5 if >=5 targets, 2 if >=2, 0 if 1 |
Compliance (0-15)
| Sub-factor | Max | Calculation |
|---|---|---|
| Approval rate | 5 | min(5, totalApproved / totalDecisions * 5) |
| Approval streak | 5 | min(5, consecutiveApprovals * 0.25) |
| Override frequency | 5 | 5 - min(5, humanOverrides * 1.67) |
Fewer human overrides = higher compliance score. Agents that consistently stay within limits score highest.
Network (0-5)
Average trust score of known counterparties, divided by 20. Agents that transact with other trusted agents earn network reputation.
Can be disabled via config: networkScore.enabled: false.
Risk Penalty (0-30, subtracted)
| Factor | Max | Trigger |
|---|---|---|
| Frequency spike | 10 | >15 requests in 60 seconds |
| Failed transactions | 5 | min(5, failedRequests * 2) |
| Inactivity decay | 5 | min(5, hoursInactive * 0.5) |
| Spend pressure | 5 | >85% daily limit + active approvals |
| Denial streak | 5 | min(5, consecutiveDenials * 2.5) |
Frequency penalty and inactivity decay rate are configurable.
Spending Tiers
| Tier | Min Score | Daily Limit | Per-Tx Limit | Color |
|---|---|---|---|---|
| Sovereign | 80 | $1,000 | $500 | #fff8e1 |
| Trusted | 60 | $200 | $100 | #ffd700 |
| Building | 40 | $50 | $25 | #4caf50 |
| Cautious | 20 | $10 | $5 | #00bcd4 |
| Restricted | 1 | $2 | $1 | #2196f3 |
| Frozen | 0 | $0 | $0 | #1a1a4e |
All limits are configurable via triage.config.json scoreBands.
API Reference
POST /api/policy/evaluate
Core policy evaluation endpoint. Called by triage-policy for every transaction.
Headers:
Content-Type: application/jsonx-policy-secret: <secret>(optional, ifPOLICY_SECRETenv is set)Signature-Input+Signature(optional, RFC 9421 Web Bot Auth)
Request:
{
"chain_id": "eip155:84532",
"wallet_id": "5dccd73e-...",
"api_key_id": "0d3531c9-...",
"transaction": {
"to": "0xRecipient...",
"value": "1000000000000000",
"raw_hex": "0x",
"data": "0x"
},
"spending": {
"daily_total": "0",
"date": "2026-04-04"
},
"timestamp": "2026-04-04T12:00:00Z"
}Response (200):
{
"allow": true,
"trustScore": 82,
"tier": "Sovereign",
"dailyLimit": 1000,
"dailySpent": 150.50,
"perTxLimit": 500
}Denial reasons:
Agent is frozen(tier check)Exceeds per-transaction limit ($500)(per-tx check)Exceeds daily spending limit ($1000)(daily check)
curl -X POST http://localhost:4021/api/policy/evaluate \
-H "Content-Type: application/json" \
-d '{"chain_id":"eip155:84532","wallet_id":"w1","api_key_id":"k1","transaction":{"to":"0xABC","value":"1000000000000000","raw_hex":"0x","data":"0x"},"spending":{"daily_total":"0","date":"2026-04-04"},"timestamp":"2026-04-04T12:00:00Z"}'POST /api/override/:address
Human override for a denied transaction. Boosts trust score by overrideBoost (default +3). Override expires after 5 minutes.
curl -X POST http://localhost:4021/api/override/0xAgentAddressResponse (200): Full AgentProfile JSON.
Response (404): { "error": "Agent not found" } or { "error": "No pending override for this agent" } (including expired overrides).
GET /api/agents
Top 20 agents sorted by trust score.
curl http://localhost:4021/api/agentsGET /api/agents/:address
Single agent profile.
curl http://localhost:4021/api/agents/0xAgentAddressPOST /api/agents/:address/pubkey
Register an agent's Ed25519 public key for Web Bot Auth (RFC 9421).
curl -X POST http://localhost:4021/api/agents/0xAgent/pubkey \
-H "Content-Type: application/json" \
-d '{"publicKey":"base64EncodedEd25519PublicKey"}'GET /api/stats
Aggregate statistics.
curl http://localhost:4021/api/statsResponse:
{
"totalAgents": 5,
"totalDecisions": 142,
"totalApproved": 128,
"totalDenied": 14
}POST /api/verify-context
World ID sign request. Returns signing parameters for client-side World ID verification. Returns 501 if WORLD_ID_RP_ID and WORLD_ID_SIGNING_KEY are not configured.
POST /api/verify-human
World ID proof verification. Verifies proof server-side via World API, stores nullifier hash.
curl -X POST http://localhost:4021/api/verify-human \
-H "Content-Type: application/json" \
-d '{"address":"0xAgentAddress"}'POST /api/x402/verify
Verify an x402 payment signature via the facilitator.
curl -X POST http://localhost:4021/api/x402/verify \
-H "Content-Type: application/json" \
-d '{"paymentHeader":"...","payTo":"0xRecipient","maxAmountRequired":"1000000"}'GET /api/x402/payment-requirements
Returns payment info for HTTP 402 responses. Requires PAY_TO_ADDRESS env var.
curl http://localhost:4021/api/x402/payment-requirementsWebSocket: /ws
Real-time event stream. Connect to ws://localhost:4021/ws.
Event types:
// Transaction decision
{ type: 'POLICY_DECISION', agent, amount, trustScore, tier, decision: 'APPROVE'|'DENY'|'OVERRIDE', reason, dailyLimit, dailySpent, timestamp }
// Trust score change (on override)
{ type: 'TRUST_CHANGE', agent, oldScore, newScore, oldTier, newTier, reason, timestamp }
// Budget warning (>80% daily limit)
{ type: 'BUDGET_WARNING', agent, spent, limit, percentage, timestamp }CLI Reference
triage-ows -- Reputation-gated spending policies for OWS agent wallets
Usage:
triage-ows init Scaffold triage.config.json + .env.example
triage-ows dev Start scoring server + dashboard (dev mode)
triage-ows register Register Triage policy with OWS
triage-ows attach Attach an OWS agent to Triage governance
triage-ows seed Populate demo agents for testing
triage-ows status Show server status + agent summary
triage-ows help Show this help
Options:
--port <number> Server port (default: 4021)
--config <path> Config file path (default: ./triage.config.json)triage-ows init
Creates triage.config.json and .env.example in the current directory. Skips if files already exist.
triage-ows dev
Starts the scoring server and dashboard. Reads config from triage.config.json.
triage-ows dev --port 8080 --config ./custom.config.jsontriage-ows register
Registers the Triage policy with OWS. Copies policy-template.json to ~/.ows/policies/triage-trust.json and runs ows policy create.
triage-ows attach
Creates an OWS API key attached to a wallet with the Triage policy.
triage-ows attach --wallet sovereign-agent --key sovereign-keyRuns ows key create --name <key> --wallet <wallet> --policy triage-trust.
triage-ows status
Shows server status and agent summary table.
triage-ows status --port 4021Library Usage
import {
// Server
app, startServer, BASE_SEPOLIA, USDC_BASE_SEPOLIA,
// Scoring
computeTrustScore, getSpendingLimits,
identityScore, onChainScore, behaviorScore,
complianceScore, networkScore, riskPenalty,
// Store
getOrCreateAgent, getAgent, getAllAgents, getTopAgents,
recordApproval, recordDenial, recordOverride,
addVerifiedHuman, isVerifiedHuman, setOWSWallet, setWebBotAuthVerified,
// Config
loadConfig, getConfig,
// Events
emitEvent, startWebSocketServer, attachWebSocketToServer,
// Web Bot Auth (RFC 9421)
verifyWebBotAuth, registerAgentPublicKey, parseSignatureInput,
// Types & Constants
getTierForScore, SPENDING_TIERS, updateSpendingTiers,
} from 'triage-ows'
import type {
PolicyContext, PolicyResult, TrustBreakdown, SpendingTier,
AgentProfile, TriageConfig,
PolicyDecisionEvent, TrustChangeEvent, BudgetWarningEvent, TriageEvent,
} from 'triage-ows'Scoring an Agent
const agent = getOrCreateAgent('0xAgentAddress')
const breakdown = computeTrustScore(agent, getAgent)
const { tier, dailyLimit, perTxLimit } = getSpendingLimits(breakdown.total)
console.log(`Score: ${breakdown.total}, Tier: ${tier.name}, Limit: $${dailyLimit}/day`)Recording Transactions
// Approved transaction
const updated = recordApproval('0xAgent', '0xRecipient', 25.50)
// Denied transaction (stores pending override)
const denied = recordDenial('0xAgent', tx, 500.00)
// Human override (expires after 5 minutes)
const overridden = recordOverride('0xAgent')Custom Scoring Server
import { app, startServer } from 'triage-ows'
import { Hono } from 'hono'
// Add custom routes to the Hono app
app.get('/api/custom', (c) => c.json({ hello: 'world' }))
// Start with custom port
startServer(8080)Custom Config
import { loadConfig, getConfig, updateSpendingTiers } from 'triage-ows'
// Load from custom path
loadConfig('/path/to/config.json')
// Read config values
const threshold = getConfig().warningThresholdXMTP Notifications
XMTP uses native bindings (@xmtp/node-sdk). It's loaded lazily to avoid crashes on systems where bindings aren't available. The server handles this automatically — manual import is only needed for advanced library usage.
// XMTP requires native bindings — import lazily
const xmtp = await import('triage-ows/dist/xmtp')
await xmtp.initXMTP()
// Send notifications
await xmtp.notifyDenial(agent, amount, score, tier, limit, spent, reason, txTo)
await xmtp.notifyTrustChange(agent, oldScore, newScore, oldTier, newTier, reason)
await xmtp.notifyBudgetWarning(agent, spent, limit)
await xmtp.notifyAnomaly(agent, pattern, action, riskPenalty, oldScore, newScore)Types Reference
PolicyContext
The input to every policy evaluation. Sent by OWS to triage-policy via stdin.
| Field | Type | Description |
|---|---|---|
chain_id |
string |
Chain identifier (e.g. eip155:84532) |
wallet_id |
string |
OWS wallet UUID |
api_key_id |
string |
OWS API key UUID (used as agent identifier) |
transaction.to |
string |
Recipient address |
transaction.value |
string |
Transaction value in wei |
transaction.raw_hex |
string |
Raw transaction hex |
transaction.data |
string |
Transaction data |
spending.daily_total |
string |
OWS-reported daily total |
spending.date |
string |
Date string (ISO) |
timestamp |
string |
Request timestamp (ISO) |
policy_config |
Record<string, unknown> |
Optional policy-level config |
AgentProfile
Complete agent state tracked by the store.
| Field | Type | Description |
|---|---|---|
address |
string |
Agent identifier (api_key_id) |
walletId |
string? |
OWS wallet UUID |
apiKeyId |
string? |
OWS API key UUID |
isOWSWallet |
boolean |
Whether registered as OWS wallet |
worldIdVerified |
boolean |
World ID human proof verified |
webBotAuthVerified |
boolean |
RFC 9421 signature verified |
nullifierHash |
string? |
World ID nullifier hash |
trustScore |
number |
Current trust score (0-100) |
breakdown |
TrustBreakdown |
Per-factor score breakdown |
tier |
string |
Current spending tier name |
totalRequests |
number |
Lifetime request count |
successfulRequests |
number |
Lifetime approved count |
failedRequests |
number |
Lifetime denied count |
dailySpent |
number |
Today's spending in USD |
dailyDate |
string |
Date of current daily tracking |
consecutiveApprovals |
number |
Current approval streak |
consecutiveDenials |
number |
Current denial streak |
totalApproved |
number |
Lifetime approved transactions |
totalDenied |
number |
Lifetime denied transactions |
humanOverrides |
number |
Total human override count |
counterparties |
string[] |
Unique transaction targets |
pendingOverride |
object? |
Pending override data with createdAt timestamp |
requestTimestamps |
number[] |
Last 100 request timestamps |
lastActive |
number |
Last activity timestamp |
consecutiveCleanDays |
number |
Days with only approved transactions |
cleanDayDate |
string |
Date of last clean day check |
createdAt |
number |
Agent creation timestamp |
TrustBreakdown
| Field | Type | Range | Description |
|---|---|---|---|
identity |
number |
0-35 | Identity verification score |
onChain |
number |
0-20 | On-chain history score |
behavior |
number |
0-20 | Transaction behavior score |
compliance |
number |
0-15 | Policy compliance score |
network |
number |
0-5 | Network trust score |
risk |
number |
0-30 | Risk penalty (subtracted) |
total |
number |
0-100 | Final composite score |
TriageConfig
| Option | Type | Default | Description |
|---|---|---|---|
scoreBands |
Array |
6 tiers | Custom tier definitions (name, min, dailyLimit, perTxLimit, color) |
warningThreshold |
number |
0.8 |
Budget warning threshold (0-1) |
xmtp.enabled |
boolean |
true |
Enable XMTP notifications |
worldId.enabled |
boolean |
true |
Enable World ID verification |
webBotAuth.enabled |
boolean |
true |
Enable RFC 9421 signature verification |
networkScore.enabled |
boolean |
true |
Enable network trust scoring |
scoring.overrideBoost |
number |
3 |
Trust score boost per human override |
scoring.maxFrequencyPenalty |
number |
10 |
Max penalty for request frequency spikes |
scoring.inactivityDecayRate |
number |
0.5 |
Trust decay per hour of inactivity |
port |
number |
4021 |
Server port |
dashboardEnabled |
boolean |
true |
Serve dashboard static files |
Configuration
triage.config.json
Create with triage-ows init or manually:
{
"warningThreshold": 0.8,
"scoreBands": [
{ "name": "Sovereign", "min": 80, "dailyLimit": 1000, "perTxLimit": 500 },
{ "name": "Trusted", "min": 60, "dailyLimit": 200, "perTxLimit": 100 },
{ "name": "Building", "min": 40, "dailyLimit": 50, "perTxLimit": 25 },
{ "name": "Cautious", "min": 20, "dailyLimit": 10, "perTxLimit": 5 },
{ "name": "Restricted", "min": 1, "dailyLimit": 2, "perTxLimit": 1 },
{ "name": "Frozen", "min": 0, "dailyLimit": 0, "perTxLimit": 0 }
],
"scoring": {
"overrideBoost": 3,
"maxFrequencyPenalty": 10,
"inactivityDecayRate": 0.5
},
"xmtp": { "enabled": true },
"worldId": { "enabled": true },
"webBotAuth": { "enabled": true },
"networkScore": { "enabled": true },
"port": 4021,
"dashboardEnabled": true
}Config is loaded from (in order):
- Path passed to
loadConfig(path) TRIAGE_CONFIG_PATHenvironment variable./triage.config.jsonin the current working directory- Built-in defaults (if no file found)
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
PORT |
No | 4021 |
Server port |
POLICY_SECRET |
No | - | Shared secret for policy endpoint authentication |
WORLD_ID_RP_ID |
No | - | World ID relying party ID |
WORLD_ID_SIGNING_KEY |
No | - | World ID signing key (hex) |
XMTP_PRIVATE_KEY |
No | - | Private key for XMTP notifications |
HUMAN_XMTP_ADDRESS |
No | - | Wallet address for XMTP DM notifications |
PAY_TO_ADDRESS |
No | - | Address for x402 payment requirements |
BASE_SEPOLIA_RPC_URL |
No | https://sepolia.base.org |
Base Sepolia RPC endpoint |
SCORING_SERVER_URL |
No | http://localhost:4021 |
Scoring server URL (used by triage-policy) |
TRIAGE_CONFIG_PATH |
No | ./triage.config.json |
Path to config file |
Generate a .env.example with triage-ows init.
Override Flow
1. Agent sends transaction
2. Policy evaluation: DENY (exceeds limit)
3. Server records pendingOverride with 5-minute TTL
4. XMTP notification sent to human owner:
"Transaction denied
Agent: 0xAgent...
Amount: $50.00 USDC -> 0xRecipient...
Trust score: 42 (Building tier)
Reason: Exceeds per-transaction limit ($25)
Reply 'override' to approve this transaction."
5. Human replies "override" in XMTP DM
6. Message listener detects reply, POSTs to /api/override/:address
7. Transaction approved, trust score boosted by +3
8. TRUST_CHANGE event emitted to dashboard
9. Override expires after 5 minutes if not acted onDashboard
The dashboard is a React SPA served from / on the scoring server.
Panels:
- Stat Cards -- Total agents, policy decisions (with approval rate), active wallets, daily volume
- Signing Decisions -- Canvas particle visualization with 3 lanes (APPROVE green, DENY red, OVERRIDE gold). Particle size scales with transaction amount. Shows running $ volume per lane.
- Agent Budgets -- Top 5 agents with progress bars showing daily spend vs limit. 80% tick marker. Tier labels.
- Policy Decisions -- Real-time feed of every transaction decision with agent, score, amount, budget usage, and denial reasons
- Trust Leaderboard -- Top 8 agents ranked by score with tier badges, score bars, daily limits, trend arrows, and World ID / Web Bot Auth verification icons
Security
- Policy Secret -- Optional shared secret (
POLICY_SECRETenv) authenticates the policy evaluation endpoint. Both the executable and server must share the same secret. - World ID -- Nullifier hashes are verified server-side via the World API. The client never self-reports verification status.
- Override Timeout -- Pending overrides expire after 5 minutes. Stale overrides cannot be replayed.
- Input Validation -- Policy evaluation validates JSON structure, handles malformed BigInt values gracefully (falls back to 0).
- XMTP Graceful Fallback -- XMTP is lazy-loaded. If native bindings are unavailable, the server starts normally with notifications disabled.
- Web Bot Auth -- Ed25519 signature verification follows RFC 9421. Public keys must be pre-registered via the API.
Features
- 5-factor trust scoring -- Identity, On-Chain, Behavior, Compliance, Network minus Risk
- 6 spending tiers -- Sovereign through Frozen with configurable limits
- Real-time dashboard -- Canvas particle flow, live feed, leaderboard, budget tracking
- XMTP notifications -- Denial alerts, budget warnings, trust changes via DM
- Human override -- Reply "override" in XMTP to approve denied transactions (+3 trust boost)
- Override timeout -- 5-minute expiry on pending overrides
- Web Bot Auth -- RFC 9421 HTTP Message Signatures for cryptographic agent identity
- World ID -- Human verification via World ID proof-of-personhood
- x402 payments -- Payment verification via x402 facilitator
- ETH price oracle -- Live CoinGecko price feed with 5-minute cache and $2,500 fallback
- On-chain balance -- Base Sepolia balance lookup via viem with 10-minute cache
- Configurable --
triage.config.jsonfor tiers, thresholds, feature flags, scoring tuning - CLI -- init, dev, register, attach, seed, status commands
- Policy secret -- Optional shared secret for endpoint authentication
- Budget warnings -- Configurable threshold (default 80%) with XMTP alerts
- WebSocket events -- Real-time POLICY_DECISION, TRUST_CHANGE, BUDGET_WARNING streams
Technologies
| Technology | Role |
|---|---|
| Hono | HTTP framework |
| viem | On-chain queries (Base Sepolia) |
| XMTP | Agent-to-human messaging (@xmtp/node-sdk) |
| World ID | Human proof-of-personhood |
| x402 | Payment verification protocol |
| ws | WebSocket server |
| React | Dashboard UI |
| Tailwind CSS | Dashboard styling |
| CoinGecko | ETH/USD price feed |
Node.js crypto |
Ed25519 signature verification (RFC 9421) |
OWS Integration
Register the Policy
# Registers triage-trust policy with OWS
triage-ows registerThis copies policy-template.json to ~/.ows/policies/triage-trust.json and runs ows policy create.
Attach an Agent
# Create an OWS API key with Triage governance
triage-ows attach --wallet my-agent --key my-agent-keyThis runs ows key create --name <key> --wallet <wallet> --policy triage-trust.
Policy Template
{
"id": "triage-trust",
"name": "Triage Reputation-Gated Policy",
"version": 1,
"rules": [
{ "type": "allowed_chains", "chain_ids": ["eip155:84532"] }
],
"executable": "./node_modules/.bin/triage-policy",
"config": {
"scoring_server": "http://localhost:4021"
},
"action": "deny"
}The action: "deny" means transactions are blocked by default if the policy executable fails or times out.
Demo & Simulation
Seed Demo Agents
npm run seedCreates 5 agents at different trust levels: Sovereign (81), Trusted (72), Building (53), Cautious (23), Restricted (~17).
Run Live Simulation
# Start server in one terminal
triage-ows dev
# Run simulation in another
npm run simulateThe simulator fires policy evaluations every 2-3 seconds with randomized amounts, occasionally triggering denials and new agent registrations.
What's Next
- Persistence -- SQLite or Postgres backend for agent state
- Multi-chain -- Extend beyond Base Sepolia to mainnet and other chains
- Full network scoring -- Graph-based trust propagation across agent networks
- Trust portability -- Export/import agent trust scores across Triage instances
- Policy marketplace -- Share and compose policy configurations
- Anomaly detection -- ML-based pattern detection triggering
notifyAnomaly - Rate limiting -- Per-agent request rate limits on the API
Future Integrations
- AgentKit — Coinbase AgentKit agent verification as an identity signal. AgentKit-created agents would receive an identity boost similar to Web Bot Auth, recognizing that the agent was created through a governed framework.
- ERC-8004 — External reputation feed integration for standardized agent identity tokens.
License
MIT