Specter

Docs · Inicio rápido

Suelta el hook, o llama al SDK. Una sola ida y vuelta.

Dos formas de conectar tu agente a la API que decide. Ambas responden allow / review / deny, con una razón en lenguaje claro y el desglose de cada señal.

1 · Hook de Claude CodeSin código — un hook PreToolUse corre antes de que el agente haga nada, así que se puede bloquear. Apúntalo a la API.
.claude/settings.json
// .claude/settings.json — one-line Claude Code hook
{
  "hooks": {
    "PreToolUse": [{
      "matcher": "*",
      "hooks": [{
        "type": "http",
        "url": "https://specter-decision-api.fly.dev/hooks/claude-code",
        "headers": { "x-api-key": "$SPECTER_API_KEY" },
        "allowedEnvVars": ["SPECTER_API_KEY"],
        "timeout": 5
      }]
    }]
  }
}
El truco (no lo “arregles”): un hook HTTP no bloquea respondiendo con un código de error. Un 4xx/5xx solo registra un error y la herramienta corre igual. El hook siempre responde HTTP 200 — el permitir/bloquear va en el cuerpo, no en el código de estado. Para bloquear, el cuerpo trae permissionDecision: "deny". Specter hace exactamente eso. (Los hooks de tipo comando bloquean con código de salida 2; el código 1 solo advierte.)
2 · SDKVerifica cualquier acción desde tu propio código.
guard.ts
// npm i specter-sdk
import { Guard } from 'specter-sdk';

const guard = new Guard({ apiUrl: process.env.SPECTER_API_URL!, apiKey: process.env.SPECTER_API_KEY! });

const decision = await guard.check({
  agentId: 'shop-agent',
  sessionId: 'sess_42',
  action: { type: 'payment', amount: 79.99, currency: 'USD',
            destination: 'acct_acme_store', merchantClaimed: 'Acme Store', rawInput: {} },
  context: { userPrompt: 'Buy the Acme mouse from Acme Store under $100.',
             destinationOrigin: 'user_prompt', establishedMerchant: 'Acme Store' },
});

if (decision.decision !== 'allow') throw new Error(decision.reason);
// ...only now move money.

Petición — POST /v1/evaluate

POST /v1/evaluate
Authorization: Bearer <SPECTER_API_KEY>
Content-Type: application/json

{
  "agentId": "shop-agent",
  "sessionId": "sess_42",
  "action": {
    "type": "payment",            // payment | refund | db_write | shell | file | other
    "amount": 79.99,
    "currency": "USD",
    "destination": "acct_acme_store",
    "merchantClaimed": "Acme Store",
    "rawInput": {}
  },
  "context": {
    "userPrompt": "Buy the Acme mouse from Acme Store under $100.",
    "destinationOrigin": "user_prompt",   // user_prompt | ingested_content | tool_output | unknown
    "sourceRefs": ["firecrawl:https://shop.example/acme"],
    "establishedMerchant": "Acme Store"
  }
}

Respuesta

200 OK
{
  "decision": "deny",             // allow | deny | review
  "riskScore": 0.78,              // 0..1
  "reason": "Blocked: payee originated from ingested_content and does not trace back to the user's request.",
  "signals": {
    "provenance": "payee originated from ingested_content ...",
    "policy": "never-before-seen account — requires human approval",
    "consistency": "destination does not correspond to claimed merchant",
    "llm": "llm: payee does not match the user's merchant; likely injected"
  },
  "signalDetail": [
    { "id": "provenance", "score": 1, "verdict": "..." },
    { "id": "policy", "score": 0.6, "verdict": "..." }
  ]
}
GET/v1/audit

Tu registro a prueba de manipulación, página por página.

GET/v1/audit/verify

Vuelve a revisar el registro para probar que no se alteró → { valid, brokenAt }.

POST/hooks/claude-code

Convierte un payload de PreToolUse en una verificación. Siempre responde 200.

Auth, límites, integraciones

  • Auth: pasa Authorization: Bearer <key> o x-api-key. Cada clave está ligada a tu cuenta.
  • Límites de tasa: por clave (por defecto 60 de ráfaga / 30 rps); recibes un 429 si te pasas.
  • Integraciones: el hook de Claude Code, specter-sdk, o un proxy delante de tus herramientas.
  • Velocidad: mantenido en caliente en Fly, menos de 500ms en el 99% de las llamadas.