Quick Start

VITNA produces Ed25519-signed evidence records that anyone can verify offline with a published public key and an open-source verifier, with no need to trust VITNA's servers. It is a cooperative guardrail with heuristic detection, and those limits are documented publicly. Its purpose is not prevention. It is independently verifiable proof that an AI agent's actions were checked and allowed.

In category terms: VITNA issues verifiable compliance receipts for agent actions. Each checked action produces a decision record, and those records are packaged into an Ed25519-signed agent action receipt that a third party can verify offline. See Verify Evidence for the two verification tiers, and What VITNA Sends for exactly what leaves your machine.

Get your agent sending events to VITNA in under 2 minutes. No SDK required, just one HTTP call.

1
Create an account
Go to vitna.costrinity.xyz/dashboard and register with your email.
2
Create an agent
Click "New Agent", give it a name, and copy your API key.
3
Send your first event
Make a POST request from your agent:
bash
curl -X POST https://vitna.costrinity.xyz/api/ingest \
  -H "Authorization: Bearer vigil_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"action": "hello-world", "data": {"message": "VITNA connected!"}}'
✓ That's it. Open your dashboard and you'll see the event appear in real-time.

REST API

The VITNA API is a simple REST API. Every endpoint accepts JSON and returns JSON.

POST /api/ingest

Log a single event from your agent. Core endpoint, call this every time your agent does something.

Headers

HeaderValue
AuthorizationBearer vigil_YOUR_KEY
Content-Typeapplication/json
javascript
await fetch('https://vitna.costrinity.xyz/api/ingest', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer vigil_YOUR_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    action: 'buy',
    data: { token: 'SOL', amount: 0.5, reason: 'momentum signal' },
    cost_sol: 0.001,
    status: 'success'
  })
})

Response

json
{
  "ok": true,
  "event_id": "550e8400-e29b-41d4-a716-446655440000",
  "storage_mode": "cloud"
}

🔒 Storage Mode

Every event request accepts a storage_mode field. When set to "local", VITNA validates your API key and enforces rate limits, but never writes to our database. Events are returned to the client and stored in your browser's IndexedDB. Nothing persists on COSTRINITY servers.

☁️ Cloud (default)
Events stored in your isolated VITNA account. Accessible from any device. 7–90 day history depending on plan.
💾 Local
Events stored in your browser's IndexedDB only. Zero server persistence. Data never leaves your machine.
javascript
// Cloud mode, stored in your VITNA account
body: JSON.stringify({ action: 'buy', storage_mode: 'cloud' })

// Local mode, VITNA never touches our DB
body: JSON.stringify({ action: 'buy', storage_mode: 'local' })
// → Server returns enriched event with ID + compliance tags
// → Your dashboard stores it in IndexedDB automatically

POST /api/ingest (batch)

Log multiple events in a single call. Up to 100 events per request.

javascript
await fetch('https://vitna.costrinity.xyz/api/ingest', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer vigil_YOUR_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    events: [
      { action: 'scan', data: { tokens: 847 } },
      { action: 'filter', data: { passed: 12 } },
      { action: 'buy', data: { token: 'BONK' }, cost_sol: 0.1 }
    ]
  })
})

GET /api/graph

Returns the activity graph for an agent, nodes are actions (or individual events), edges are causal links inferred from parent_id / trace_id / run_id / temporal proximity. Used by the dashboard’s Activity Graph.

agent_id*UUIDRequired.
owner_id*UUIDRequired. Must own the agent.
group"action"|"event"Default 'action'. 'event' returns one node per event (raw timeline).
limitint 1–5000Default 2000. Caps the number of events scanned.
include_noise0|1Default 0. When 0, baseline events (heartbeat, ping, health-check, *-heartbeat, idle_*) are filtered server-side BEFORE the temporal-proximity edge calculation. The dashboard passes 1 to keep the count badge live; CLI consumers usually want the default.
include_system0|1Default 0. When 1, VITNA-internal cron events (heartbeat_missed, budget_warning, key_rotated, …) are included.
bash
# Default, actions only, clean edges
curl "https://vitna.costrinity.xyz/api/graph?agent_id=...&owner_id=..."

# Include baseline noise (what the dashboard does)
curl "https://vitna.costrinity.xyz/api/graph?agent_id=...&owner_id=...&include_noise=1"
json
{
  "nodes": [
    { "id": "buy", "label": "buy", "count": 47, "color": "#4ade80", "size": 7, ... }
  ],
  "edges": [
    { "from": "scan", "to": "buy", "weight": 12, "kind": "trace" }
  ],
  "stats": {
    "event_count": 1284,
    "action_count": 6,
    "edge_count": 14,
    "window_hours": 24.0,
    "noise_event_count": 847,    // events filtered out as baseline noise
    "noise_action_count": 1      // distinct noise action types (e.g. "griid-heartbeat")
  },
  "agent": { "id": "...", "name": "trading-bot-alpha" }
}

TypeScript SDK

Not yet published. The typed SDK is planned and is not on npm, so there is nothing to install today. The shape below is a design preview and may change. The package we do publish is @costrinity/vitna-compliance-mcp (the MCP server, see the MCP section). Until the SDK ships, use the REST API directly, which needs no dependency at all.

Planned: an optional typed wrapper around the REST API adding retry logic, batching, and type safety.

typescript
// PLANNED API, not yet published on npm.
import { Vitna } from '@costrinity/vitna'

const vitna = new Vitna({
  apiKey: process.env.VITNA_API_KEY!,
  // Optional: set a default agent name
  // agent: 'support-copilot',
})

// Pre-flight: check a risky action before the agent runs it.
const decision = await vitna.preflight({
  action: 'db.query',
  target: 'DELETE FROM users WHERE id = 42',
  scope: 'in_scope',
})
if (decision.effect === 'block') {
  throw new Error(decision.reason)
}

// Log a completed action for the signed audit trail.
await vitna.log({
  action: 'email.send',
  data: { to: 'customer@example.com', template: 'refund_confirmation' },
  status: 'success',
})

// Log an error
await vitna.log({
  action: 'tool.call',
  data: { name: 'issue_refund', message: 'Amount exceeds policy cap', code: 'ERR_POLICY' },
  status: 'error',
})

// Batch log, efficient for high-frequency agents
await vitna.logBatch([
  { action: 'tool.call',  data: { name: 'search_orders' } },
  { action: 'tool.call',  data: { name: 'issue_refund' } },
  { action: 'file.write', data: { path: '/reports/q3.csv' } },
])

ElizaOS Plugin

Zero-config plugin for ElizaOS agents. Automatically logs every action your agent takes.

Not yet published. The ElizaOS plugin is not on npm yet, so the install below will not resolve today. It is shown as a design preview. Use the REST API or @costrinity/vitna-compliance-mcp in the meantime.
bash
# PLANNED, not yet published on npm npm install @costrinity/vitna-eliza-plugin
typescript
import vitnaPlugin from '@costrinity/vitna-eliza-plugin';

export const character = {
  name: 'SupportAgent',
  plugins: [
    vitnaPlugin({
      apiKey: process.env.VITNA_API_KEY,
      // Optional: custom agent name (defaults to character.name)
      agentName: 'my-elizaos-bot',
    }),
  ],
  // All agent actions are automatically captured:
  // - Tool calls and responses
  // - Decision reasoning
  // - Memory reads/writes
  // - Errors and retries
};
💡 The ElizaOS plugin hooks into the agent runtime automatically. You don't need to add any vitna.log() calls manually.

MCP Proxy

For agents that use the Model Context Protocol (MCP). The proxy sidecars any MCP server, captures every tool call, resource read, prompt fetch, and notification as a VITNA event. Zero changes to your agent code.

bash
npm install -g @costrinity/vitna-mcp

Add it to your MCP client config (Claude Desktop, Claude Code, Cursor, etc.):

json
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": [
        "@costrinity/vitna-mcp", "--",
        "npx", "@modelcontextprotocol/server-filesystem", "/path/to/files"
      ],
      "env": {
        "VITNA_API_KEY":  "vigil_...",
        "VITNA_AGENT_ID": "claude-desktop-filesystem"
      }
    }
  }
}
💡 Captured events appear as mcp.tools.call, mcp.resources.read, etc. with request params, response result, and duration on the same event. The proxy is fire-and-forget: VITNA outages never block the MCP flow.

Event Schema

Full schema of all fields accepted by the ingest endpoint.

FieldTypeDescription
action*stringWhat the agent did. Free-form string: 'buy', 'error', 'decision', 'transfer', etc.
dataobjectAny additional context. JSON object, up to 64KB. Fully searchable in dashboard.
cost_solnumberSOL spent on this action (for P&L tracking). Supports up to 9 decimal places.
statusstring'success' | 'error' | 'info' | 'warning'. Defaults to 'info'.
iduuidAuto-generated UUID for the event. Returned in response.
agent_iduuidResolved from your API key. You don't need to pass this.
timestamptimestamptzAuto-set to current UTC time. Cannot be overridden.

Alerts

Smart alerts watch your agents for you. Configure them from the dashboard, no code needed.

Silence DetectionMost Popular

Trigger when an agent hasn't sent an event in N minutes. Catches stuck, crashed, or looping agents.

Error Rate

Alert when error events exceed X% of total events in a rolling window.

SOL Spend ThresholdCritical

Alert when cost_sol total exceeds your limit in 1 hour. Protects against runaway spending.

Custom Action Match

Alert when a specific action appears, e.g. fire immediately if action === 'emergency-stop'.

Delivery channels

  • Email (all plans)
  • Webhook POST (Pro+)
  • Slack webhook (Enterprise)

Webhooks

VITNA can POST to your endpoint when an alert fires. Available on Pro and above.

json
// Example webhook payload
{
  "event": "alert.fired",
  "alert_type": "silence",
  "agent_id": "agt_abc123",
  "agent_name": "sniper-v5",
  "message": "Agent has been silent for 10 minutes",
  "fired_at": "2026-03-24T18:00:00Z",
  "dashboard_url": "https://vitna.costrinity.xyz/dashboard/agt_abc123"
}

Webhook requests include a X-VITNA-Signature header (HMAC-SHA256) so you can verify authenticity. Configure your endpoint URL in the Dashboard → Alerts → Settings.

Rate Limits

Rate limits are per API key. Exceeded requests return 429.

PlanRate LimitEvents / MonthHistory
Free100 req/min10k/month7 days
Pro1,000 req/min1M/month90 days
EnterpriseUnlimitedUnlimitedCustom
💡 Batch requests via the events[] array count as 1 request regardless of event count. Use batching to maximize throughput on high-frequency agents.

Verify VITNA evidence yourself

One minute, no account, no trust in VITNA's servers required. Download the open-source verifier and a real signed sample bundle, then check the signature offline with Node 18 or newer:

curl -sO https://raw.githubusercontent.com/COSTRINITY/vitna-compliance-mcp/main/verify-evidence.mjs
curl -sO https://vitna.costrinity.xyz/sample-evidence.json
node verify-evidence.mjs sample-evidence.json

How verification works, in two tiers

Tier 1, the evidence package. A detached Ed25519 signature covers the whole package. Anyone can verify it offline with our published public key, with no VITNA account and no VITNA secret. This is the tier that supports non-repudiation of issuance.

Tier 2, each individual record. Every decision record is committed by a sha256 hash over its canonical JSON, and that ordered list of hashes sits inside the Ed25519-signed package. A third party can therefore verify each record, not just the package: recompute the record's hash and confirm it matches at its position. The verifier does this automatically and reports PASS or FAIL per record.

Separately, each record is integrity protected at write time with HMAC-SHA256. That is an internal check, verifiable only by VITNA because HMAC is symmetric, so we never describe it as independently verifiable or as non-repudiation. Bundles exported before the per-record hashes shipped still verify at the package level, and the verifier says so.

A VALID result proves the package was issued by VITNA and has not been altered since export, and that every record matches its committed hash. It does not prove the actions were performed or that the records are factually true. Change any byte in any record and that record reports FAIL and the overall verdict is INVALID.

Recomputing payload_sha256 (the pfa-v2 scheme)

Each decision record carries a payload_sha256 and a canon_version of pfa-v2. Here is the exact scheme so you can recompute it yourself. It is a sha256 (hex) over twelve fields joined with the pipe character, in this order, UTF-8 encoded, with no whitespace and no trailing separator. Null or absent values become the empty string.

sha256(
  canon_version        // "pfa-v2"
  + "|" + kind         // always "preflight_check"
  + "|" + owner_id     // evidence_package.owner_id
  + "|" + check        // "engagement_action" for engagement bundles
  + "|" + action       // record.action, "" if null
  + "|" + category     // engagement: evidence_package.session_id
  + "|" + decision     // record.decision
  + "|" + flagged      // "1" if decision !== "allow", else "0"
  + "|" + reason       // record.reason, "" if null
  + "|" + principal_id // "" for engagement bundles
  + "|" + effect       // record.effect
  + "|" + signed_at    // record.signed_at
)

Worked example, taken verbatim from the published sample-evidence.json (record 0). Canonical string:

pfa-v2|preflight_check|f46ba5dc-b77b-4fe0-ae3d-55e6204e3d66|engagement_action|dns.read example.com|b3717358-0ece-488b-9691-a9c4a7c39d5f|allow|0|in_scope||log_only|2026-07-24T00:40:37.048Z

sha256 -> 2b0f0d22a1a3cb4980981a12e67fa72e778ca3b0d21d322ca90eac1f578e1b2f

That matches the payload_sha256 on record 0 of the published sample. Note the two consecutive pipes before log_only: that is the empty principal_id.

Being precise about what this does and does not give you. payload_sha256 is a digest, not a signature, so recomputing it proves the record fields are internally consistent, not that VITNA issued them. The per-record assurance that a third party can rely on is record_hashes, because those sit inside the Ed25519-signed package. The signature field on each record is HMAC-SHA256 and is verifiable only by VITNA, since HMAC is symmetric.

Key transparency

The signing key is Ed25519, key_id 01833acd46d06ab4. The public key is published in more than one place, including infrastructure we do not control, so the copies can be compared:

Because the verifier ships with the key embedded and the same key is published on GitHub and npm, a mismatch between any of those copies and our API would be publicly visible. We are stating the honest limit here too: this is multi-location publication, not a formal transparency log or a third-party notary.

What VITNA sends and stores

Connecting the MCP server sends data to VITNA's cloud. For a compliance product that has to be explicit, so here it is.

What is sent on a check

Whatever you pass to the tool. For action_preflight that is the proposed action text, an optional structured payload, and an optional short action-type label, plus your owner id and API key in headers. For the compliance checks it is the fields that check needs, for example purpose and data categories.

What is stored

Less than what is sent. The signed decision record stores the action type label, the threat category and the pattern name that matched, the verdict and reason, timestamps, and owner and session identifiers. The raw action text and raw payload are not stored. Data principals are referenced by UUID only, and detected secrets and PII are redacted before anything is written.

Retention by tier, against the six-month baseline

We cite the EU AI Act requirement to retain automatically generated logs for at least six months. Stated plainly, here is which tiers meet that on their own and which do not:

TierEvent payloadsIncident recordsMeets 6-month log baseline?
Free7 days30 daysNo
Pro90 days1 yearNo, not for event payloads
Team90 days1 yearNo, not for event payloads
EnterpriseCustom per MSACustom per MSAOnly if contracted at 6 months or more

No tier below Enterprise retains event payloads in VITNA for six months, so do not rely on our hosted retention alone to satisfy a six-month obligation. The way any tier satisfies it is by exporting the Ed25519-signed evidence package and keeping it yourself. Once exported it is a self-contained, independently verifiable artifact that does not depend on our retention or on our servers continuing to exist.

Trial keys, and the zero-cloud alternative

With no credentials, the first MCP tool call self-provisions a restricted trial key against our API and caches it at ~/.vitna/credentials.json. That creates an account on our side. Trial keys are capped, return label-only results, and do not persist signed evidence until the account is claimed.

If you do not want events leaving your machine, use local mode: with storage_mode=local events stay on the operator's device and VITNA stores nothing server-side. The desktop app is the packaged form of that. The tradeoff is honest: in local mode we cannot produce a server-signed evidence package for you, because signing happens where the key is.

FAQ

Is VITNA a verifiable compliance receipt system?

Yes, for the signed evidence package model. Each checked action produces a decision record, and those records are packaged into an Ed25519-signed evidence package that functions as a verifiable compliance receipt: a third party can confirm offline, with our published public key and an open-source verifier, that the receipt was issued by VITNA, has not been altered, and that each individual record matches its committed hash. The documented limits still apply. Detection is heuristic pattern matching, VITNA is a cooperative guardrail the agent calls rather than an enforcement sandbox, and a valid receipt proves an action was checked and what the verdict was, not that the action was actually performed or that the underlying records are factually true.

Can I verify a single decision record on its own?

Yes. Every record is committed by sha256 over its canonical JSON inside the Ed25519-signed package, so you recompute that hash and confirm it appears at the right position in the signed list. The bundled verifier does this for you and prints PASS or FAIL per record.

Does VITNA block dangerous actions?

No, and we will not claim otherwise. VITNA evaluates and records, and returns allow, block, or hold. Your system enforces the decision out of process. Its purpose is not prevention, it is independently verifiable proof that an action was checked and what was decided.