@gjallarhorn-hq/sdk
v1.0.1
Published
Official TypeScript/Node.js SDK for the Gjallarhorn prompt-injection detection API
Maintainers
Readme
@gjallarhorn-hq/sdk
Official TypeScript/JavaScript SDK for the Gjallarhorn prompt-injection detection API.
Installation
npm install @gjallarhorn-hq/sdk
# or
pnpm add @gjallarhorn-hq/sdk
# or
yarn add @gjallarhorn-hq/sdkRequirements: Node.js 18+, or any environment with the global fetch API.
Quickstart
import { GjallarhornClient } from '@gjallarhorn-hq/sdk';
const client = new GjallarhornClient({ apiKey: process.env.GJALLARHORN_API_KEY });
// Scan user input before sending to your LLM
const result = await client.scan(userInput);
if (result.risk_level === 'critical' || result.risk_level === 'high') {
// block or flag the request
}
// safe to pass to LLMMethods
scan(content, opts?)
Scan a text string for prompt injection.
const result = await client.scan('Some user input', {
useClassifier: true, // force LLM classifier (default: auto)
});
console.log(result.risk_level); // 'safe' | 'low' | 'medium' | 'high' | 'critical'
console.log(result.recommendation); // 'allow' | 'flag' | 'block'
console.log(result.detection_layers); // string[], e.g. ["l1"] or ["l1", "l3"]
console.log(result.risk_score); // 0.0–1.0scanMultimodal(file, mimeType, filename?)
Scan a PDF, image, or QR code for embedded injection payloads.
import { readFileSync } from 'fs';
const pdf = readFileSync('user-upload.pdf');
const result = await client.scanMultimodal(pdf, 'application/pdf', 'upload.pdf');
console.log(result.risk_level); // same as scan()
console.log(result.billed_pages); // non-blank pages counted for billing
console.log(result.extraction_source); // 'pdf-parse' | 'mistral-ocr' | 'jsqr'Supported MIME types: application/pdf, image/png, image/jpeg, image/gif, image/webp.
getQuota()
Check your API key's current usage and quota.
const usage = await client.getQuota();
console.log(usage.tier); // 'starter' | 'growth' | 'scale' | 'enterprise'
console.log(usage.scans_this_month); // credits consumed
console.log(usage.quota_remaining); // null = unlimited
console.log(usage.quota_reset_at); // ISO 8601 timestampcheckCanary(llmResponse) — L2 Agentic Integrity
Check whether your LLM's response still contains the Gjallarhorn canary token. Absence indicates a possible system-prompt override. L2 is opt-in — it only covers agents explicitly registered with Gjallarhorn; LLM calls made outside the integration are not monitored.
const check = await client.checkCanary(llmOutput);
if (check.alert_code === 'RAGNARÖK') {
// Canary absent — system prompt may have been overridden.
// Treat this session as potentially compromised.
throw new Error('Prompt injection detected in agent output');
}Privacy: L2 canary checks are stateless and content-free. Gjallarhorn receives the LLM output, performs a token presence check, and returns a boolean result. No output content, user data, or conversation text is retained server-side.
Full L2 integration example
import { GjallarhornClient } from '@gjallarhorn-hq/sdk';
import {
AuthError,
ServiceUnavailableError,
type CanaryCheckResult,
} from '@gjallarhorn-hq/sdk';
const client = new GjallarhornClient({ apiKey: process.env.GJALLARHORN_API_KEY! });
// Step 1: Register agent once at startup — store canaryToken in your config/secrets.
// (Registration is a one-time REST call to POST /v1/agents/register — not in SDK yet.)
const CANARY_TOKEN = process.env.AGENT_CANARY_TOKEN!;
// Step 2: Inject canary into every system prompt.
function buildSystemPrompt(basePrompt: string): string {
return `${basePrompt}\n${CANARY_TOKEN}`;
}
// Step 3: After every LLM call, check output integrity.
async function checkAgentOutput(llmOutput: string): Promise<void> {
let check: CanaryCheckResult;
try {
check = await client.checkCanary(llmOutput);
} catch (err) {
if (err instanceof AuthError) throw new Error('Canary check: invalid API key');
if (err instanceof ServiceUnavailableError) {
// Canary service down — decide whether to fail-open or fail-closed
console.warn('Canary check unavailable — proceeding with caution');
return;
}
throw err;
}
if (check.alert_code === 'RAGNARÖK') {
// Canary absent: model did not echo the token it should always produce.
// Possible causes: system prompt was overridden by an injection attack.
throw new Error(
`[RAGNARÖK] Agent output integrity check failed for agent ${check.agent_id} at ${check.checked_at}`
);
}
}
// Usage in your agent loop:
const systemPrompt = buildSystemPrompt('You are a helpful customer support agent.');
const llmOutput = await callYourLLM(systemPrompt, userMessage);
await checkAgentOutput(llmOutput);
// Safe to proceedhealth()
Check server and database health. No authentication required. The endpoint is /health
(not /v1/health — note the absence of the /v1 prefix).
const status = await client.health();
console.log(status.status); // 'ok' | 'error'
console.log(status.db); // 'ok' | 'unreachable'Returns 503 if the database is unreachable.
Error Handling
All methods throw typed errors:
import {
AuthError,
QuotaExceededError,
RateLimitError,
ExtractionFailedError,
ServiceUnavailableError,
} from '@gjallarhorn-hq/sdk';
try {
const result = await client.scan(input);
} catch (err) {
if (err instanceof AuthError) console.error('Invalid API key');
if (err instanceof QuotaExceededError) console.error('Monthly quota hit — upgrade plan');
if (err instanceof RateLimitError) console.error(`Rate limited — retry after ${err.retryAfter}s`);
if (err instanceof ServiceUnavailableError) console.error('Service down');
}The SDK automatically retries on RateLimitError (respecting retry_after) and 503 Service Unavailable (exponential backoff), up to maxRetries times (default: 3).
Configuration
const client = new GjallarhornClient({
apiKey: 'gjh_...',
baseUrl: 'https://gjallarhorn.watch', // default
maxRetries: 3, // default
});TypeScript Types
All response types are exported:
import type {
ScanResult,
MultimodalScanResult,
QuotaResult,
CanaryCheckResult,
HealthResult,
PatternMatch,
} from '@gjallarhorn-hq/sdk';CanaryCheckResult includes alert_code?: 'RAGNARÖK' — present only when canary_present is false.
License
MIT — see LICENSE.
