ArmorIQ SDK (TypeScript)
Build secure AI agents with cryptographic intent verification.
The ArmorIQ SDK enables developers to build AI agents with built-in security and auditability. Just one API key - no cloud complexity.
Why ArmorIQ?
- Simple - Just one API key, no cloud credentials
- Secure - Cryptographic verification for every action
- Auditable - Complete execution trail
- Fast - Get started in 5 minutes
Installation
npm install @armoriq/sdk
# or
yarn add @armoriq/sdk
Quick Start
1. Get Your API Key
Visit platform.armoriq.ai to generate your API key.
2. Initialize the Client
import { ArmorIQClient } from '@armoriq/sdk';
const client = new ArmorIQClient({
apiKey: 'ak_your_key_here',
userId: 'your-user-id',
agentId: 'your-agent-id',
});
3. Capture Your Plan
const plan = {
goal: 'Get weather forecast',
steps: [
{
action: 'get_weather',
tool: 'weather_api',
mcp: 'weather-mcp',
inputs: { city: 'Boston' },
},
],
};
const planCapture = client.capturePlan('gpt-4', "What's the weather in Boston?", plan);
4. Get Intent Token
const token = await client.getIntentToken(planCapture);
5. Invoke Actions
const result = await client.invoke('weather-mcp', 'get_weather', token, {
city: 'Boston',
});
console.log(result);
Environment Variables
# Required
ARMORIQ_API_KEY=ak_your_key_here
USER_ID=your-user-id
AGENT_ID=your-agent-id
# Optional
ARMORIQ_ENV=production # or 'local' for local development
CONTEXT_ID=default
ARMORIQ_BACKEND_URL=https://api.armoriq.ai # read before BACKEND_ENDPOINT, by the CLI and the client
PROXY_ENDPOINT=https://proxy.armoriq.ai
IAP_ENDPOINT=https://iap.armoriq.ai
Unset, PROXY_ENDPOINT and IAP_ENDPOINT follow the backend: a production,
staging or local backend gets that environment's proxy and IAP. Any other
backend needs both set, or the first call that uses one fails naming the
missing variable.
Native OpenTelemetry observability
Version 0.7.0 has one native OpenTelemetry span tree per session. It supports exactly three exporter choices. Organization policy still governs every choice; when observability is off or policy authority is unavailable, the SDK does not export telemetry and agent execution continues.
1. ArmorIQ OTLP
ArmorIQ OTLP is the default. No endpoint configuration is needed.
const session = client.startSession({ mode: 'sdk' });
2. Generic external OTLP endpoint
Use a standards-compatible HTTP/protobuf endpoint through options or standard OpenTelemetry environment variables.
const client = new ArmorIQClient({
apiKey: 'ak_your_key_here',
observability: {
exporter: 'external',
endpoint: 'https://collector.example.com/v1/traces',
headers: { Authorization: 'Bearer token' },
},
});
OTEL_SERVICE_NAME=my-agent
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://observability.example.com/v1/traces
OTEL_EXPORTER_OTLP_TRACES_HEADERS=Authorization=Bearer%20token,x-tenant-id=my-tenant
You can instead supply a base endpoint. The SDK appends /v1/traces only for
this base form:
OTEL_EXPORTER_OTLP_ENDPOINT=https://collector.example.com
OTEL_EXPORTER_OTLP_HEADERS=Authorization=Bearer%20token
Trace-specific settings take precedence over general settings:
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT > OTEL_EXPORTER_OTLP_ENDPOINT
OTEL_EXPORTER_OTLP_TRACES_HEADERS > OTEL_EXPORTER_OTLP_HEADERS
OTEL_EXPORTER_OTLP_TRACES_PROTOCOL > OTEL_EXPORTER_OTLP_PROTOCOL
OTEL_EXPORTER_OTLP_TRACES_TIMEOUT > OTEL_EXPORTER_OTLP_TIMEOUT
3. Caller-owned tracer provider
Pass your own OpenTelemetry tracer provider when your application owns provider lifecycle and exporter setup.
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
const provider = new NodeTracerProvider();
const session = client.startSession({
observability: { exporter: 'provider', tracerProvider: provider },
});
Only http/protobuf is supported for the SDK-managed external exporter. gRPC,
OTLP/JSON, compression, HTTP proxy configuration, and mTLS configuration are
not supported by that exporter.
Set those features on an OpenTelemetry Collector when they are required. The
exporter is fail-open: a remote outage, bad response, or timeout never changes
the result of an agent request. content-type headers are ignored because the
SDK always sends application/x-protobuf.
Before the first external trace, the SDK waits up to 1.4 seconds for the organization policy authority. This one-time bound prevents unauthorized data from reaching a third party. If authority is not available within the bound, external telemetry stays disabled for that session and the agent continues.
One SDK runtime has one exporter choice. For fan-out to ArmorIQ and external tools, use an OpenTelemetry Collector to route permitted traces.
Langfuse example
Langfuse is one OTLP-compatible destination. Configure its OTLP endpoint and standard headers in the environment; do not add Langfuse-specific runtime code to your agent:
OTEL_SERVICE_NAME=my-agent
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://cloud.langfuse.com/api/public/otel/v1/traces
OTEL_EXPORTER_OTLP_TRACES_HEADERS=Authorization=Basic%20BASE64_PUBLIC_KEY_COLON_SECRET_KEY,x-langfuse-ingestion-version=4
For another vendor, use that vendor's documented OTLP trace endpoint and headers. The SDK does not infer vendor URLs, credentials, or private fields.
What is exported externally
External OTLP export always includes the trace hierarchy, stable span names, service identity, supported GenAI model and token-use attributes, and safe resource attributes. API keys, policy payloads, private ArmorIQ IDs, and arbitrary user metadata are never exported to an external destination.
Prompt content, outputs, and tool arguments/results are withheld by default.
They are exported to an external destination only when the organization's
policy lease grants both content capture and external content capture, and
the effective capture mode is enhanced or debug — every other combination
(including a policy fetch that is not currently authoritative) withholds
them. Do not rely on external export being content-free by default if your
organization runs with those grants and capture modes enabled; check your
organization's observability policy if that guarantee matters to you.
Supported safe resource attributes are:
OTEL_SERVICE_NAME=my-agent
OTEL_RESOURCE_ATTRIBUTES=service.namespace=education,deployment.environment.name=staging
Do not put secrets or user content in resource attributes. Unsupported resource attributes, malformed attributes, and values that look like a phone number, SSN, or payment-card number are ignored before export.
Capture modes and per-session opt-out
The capture modes are exactly off, metadata, enhanced, and debug.
Select an upper bound with ARMORIQ_OTEL_CAPTURE_MODE or
observability.captureMode; the organization policy can only narrow it.
Streamed chat requests include stream_options: { include_usage: true } by
default (otel.streamUsage, ARMORIQ_OTEL_STREAM_USAGE, default on) so token
usage arrives on a usage-only chunk that is hidden from the caller. Set
ARMORIQ_OTEL_STREAM_USAGE=0 (or observability.streamUsage: false) when a
gateway rejects stream_options or the raw stream must stay untouched. An
explicit stream_options on the request is never changed.
const noTelemetrySession = client.startSession({
observability: { enabled: false },
});
Transcript token usage
Token usage is stored per session, model, UTC date and UTC hour. Each message
counts in the hour of its own timestamp. Pass the session's native OTel bridge
when you record transcript usage so model tokens become gen_ai.chat
spans on the same trace.
await client.captureTranscriptTokens({
transcriptPath: '/tmp/transcript.jsonl',
product: 'armorclaude',
sessionId: session.sessionId,
otel: session.otelSession,
});
To post a Claude Code session yourself, including its subagent transcripts,
summarize it by hour and send one recordTokenUsage call per hour.
usageDate (YYYY-MM-DD) and usageHour (0-23) are required; a call without
a valid pair returns { ok: false, reason } and posts nothing.
import { summarizeSessionUsageByHour } from '@armoriq/sdk';
const seen = new Set<string>();
const { hours, repo } = summarizeSessionUsageByHour(mainTranscriptPath, { seen });
for (const { usageDate, usageHour, entries } of hours) {
await client.recordTokenUsage({
product: 'armorclaude',
sessionId,
usageDate,
usageHour,
entries,
repo,
});
}
Each post replaces the row for that session, model, date, hour and device. Share
one seen set across sessions so history copied into a forked session counts
once.
0.7.0 breaking changes
The prior pipeline selector, JSON recorder and shipper APIs, recorder handles,
schema and trace-summary exports, and defaultObservabilityConfig are removed.
Use session.otelSession for native spans. See the 0.7.0 breaking-change notes
in CHANGELOG.md for the complete removed public surface.
Advanced Usage
Delegation
import * as crypto from 'crypto';
// Generate delegate keypair
const { publicKey, privateKey } = crypto.generateKeyPairSync('ed25519');
const pubKeyHex = publicKey.export({ type: 'spki', format: 'der' }).toString('hex');
const delegation = await client.delegate(token, pubKeyHex, 1800);
console.log(`Delegated token: ${delegation.delegatedToken.tokenId}`);
Custom Proxy Endpoints
const client = new ArmorIQClient({
apiKey: 'ak_your_key_here',
userId: 'user-123',
agentId: 'agent-456',
proxyEndpoints: {
'weather-mcp': 'https://weather-proxy.example.com',
'finance-mcp': 'https://finance-proxy.example.com',
},
});
Error Handling
import {
InvalidTokenException,
MCPInvocationException,
PolicyBlockedException,
TokenExpiredException,
} from '@armoriq/sdk';
try {
const result = await client.invoke('weather-mcp', 'get_weather', token, { city: 'Boston' });
} catch (error) {
if (error instanceof TokenExpiredException) {
console.error('Token expired, please get a new one');
} else if (error instanceof PolicyBlockedException) {
// The backend refuses a step outside the anchored plan, or one a policy blocks.
console.error('Blocked:', error.reason);
} else if (error instanceof MCPInvocationException) {
console.error('MCP invocation failed:', error.message);
}
}
API Reference
ArmorIQClient
Constructor Options
interface SDKConfig {
apiKey: string; // Required: Your API key
userId: string; // Required: User identifier
agentId: string; // Required: Agent identifier
contextId?: string; // Optional: Context identifier (default: 'default')
useProduction?: boolean; // Optional: Use production endpoints (default: true)
iapEndpoint?: string; // Optional: Override IAP endpoint
proxyEndpoint?: string; // Optional: Override proxy endpoint
backendEndpoint?: string; // Optional: Override backend endpoint
proxyEndpoints?: Record<string, string>; // Optional: MCP-specific proxies
timeout?: number; // Optional: Request timeout in ms (default: 30000)
maxRetries?: number; // Optional: Max retry attempts (default: 3)
verifySsl?: boolean; // Optional: Verify SSL (default: true)
}
Methods
capturePlan(llm, prompt, plan, metadata?)- Capture an execution plangetIntentToken(planCapture, policy?, validitySeconds?)- Get a signed intent tokeninvoke(mcp, action, intentToken, params?, userEmail?)- Invoke an MCP actiondelegate(intentToken, delegatePublicKey, validitySeconds?, allowedActions?, targetAgent?, subtask?)- Delegate to another agentverifyToken(intentToken)- Verify a token locallyclose()- Cleanup resources
Documentation
For complete documentation, visit docs.armoriq.ai
For native framework adapter versions, lifecycle hooks, callback chaining, and known limits, see the framework compatibility matrix.
For the OTLP-only 0.7.0 release sequence, see the release runbook.
Links
License
MIT License - see LICENSE file for details.