Security & governance

Pipeworx is open by default — anonymous callers can hit any of the 605 packs, no signup. That’s intentional for agent discovery. But once you’re on an OAuth or paid account, you get a set of self-service controls for narrowing what a managed API key can do, watching what it actually did, and defending against prompt-injection payloads coming back from upstream tools.

This page lists the controls + the endpoints to read/write them.

At a glance

ControlTierWhere
Pack allowlistOAuth, paidGET/POST registry.pipeworx.io/auth/allowlist
Per-pack daily quotasOAuth, paidGET/POST registry.pipeworx.io/auth/pack-quotas
Audit-log exportOAuth, paidGET registry.pipeworx.io/auth/audit
Response-side scan (safe_mode)Any tier`?safe_mode=annotate
Tool-schema version pinningAny tierversion field on every entry in tools/list

All endpoints accept either a session cookie (set when you sign in at pipeworx.io) or a Authorization: Bearer pwx_... header carrying your managed API key.

Pack allowlist

Scope a managed API key to a subset of pack slugs. Tools in any pack not on the list reject with a tool_not_allowed envelope; tools/list filters down so the agent’s discovery view matches what it can actually call.

# Read current allowlist
curl -H "Authorization: Bearer pwx_..." \
  https://registry.pipeworx.io/auth/allowlist

# Allow only sec-events, edgar, and fred
curl -X POST -H "Authorization: Bearer pwx_..." \
  -H "Content-Type: application/json" \
  -d '{"allowed_packs": ["sec-events", "edgar", "fred"]}' \
  https://registry.pipeworx.io/auth/allowlist

# Unrestricted (default)
curl -X POST -H "Authorization: Bearer pwx_..." \
  -H "Content-Type: application/json" \
  -d '{"allowed_packs": null}' \
  https://registry.pipeworx.io/auth/allowlist

Meta-tools (ask_pipeworx, discover_tools, recall, remember, forget, …) are always allowed regardless — they’re how the agent navigates the allowed subset, so blocking them would defeat the purpose. Internal _intel compound sub-calls are exempt too: the caller already passed the gate on the parent tool.

Reads from KV on every call, no DB round-trip. Allowlist changes take effect on the next request — no propagation lag.

Per-pack daily quotas

The tier-wide rate limit protects gateway availability; per-pack quotas protect your account’s budget against a single noisy pack. Useful when you’ve handed a key to an agent that occasionally loops on an expensive call (polymarket_arbitrage, sec_xbrl_facts, etc.).

# Cap polymarket to 500/day and sec-xbrl to 200/day
curl -X POST -H "Authorization: Bearer pwx_..." \
  -H "Content-Type: application/json" \
  -d '{"pack_quotas": {"polymarket": 500, "sec-xbrl": 200}}' \
  https://registry.pipeworx.io/auth/pack-quotas

When a pack quota is reached, callers get a pack_quota_exceeded envelope naming the pack, limit, used count, and reset time — distinct error class from the global rate_limited so agents can distinguish “this pack is paused” from “the whole account is paused.” Quotas reset at UTC midnight, same window as the tier counter.

Quota values must be integers in [1, 1,000,000]. Pack slugs must match /^[a-zA-Z0-9_-]+$/.

Audit-log export

Export your own tool-call history from Pipeworx’s analytics. Use for compliance, cost reconciliation, or debugging an agent that’s making unexpected calls.

# Last 24h, max 100 events
curl -H "Authorization: Bearer pwx_..." \
  "https://registry.pipeworx.io/auth/audit?hours=24&limit=100"

Returns events with timestamp, pack, tool, tier, status (success/user_error/upstream_throttled/error), error, user_agent, latency_ms, status_code, credit_cost. Window: hours 1–720 (30d max). Limit: 1–1000.

Response-side scan: safe_mode

Opt-in defense-in-depth scanner for prompt-injection content returned by upstream tools. Off by default — set per-request via query param ?safe_mode= or header X-Pipeworx-Safe-Mode:.

Three modes:

ModeBehavior
annotateScan + attach findings to _meta.safe_mode. Body unchanged. Caller decides what to do.
redactSame scan, hits replaced inline with [REDACTED:<class>]. Body mutated.
blockOn any finding, replace the body with a safe_mode_blocked error envelope. Most conservative.

Pattern classes detected:

  • hidden_unicode — zero-width chars, bidi overrides, Unicode tags (U+E0000–E007F, the “ASCII smuggling” range)
  • instruction_override — “ignore previous instructions” family, “you are now”, new-system-prompt pivots
  • fake_system_marker — text pretending to be a privileged delimiter (<|system|>, <<<SYSTEM>>>, ```system)
  • exfil_markdown_image — markdown image whose URL embeds a query string (the image-exfil vector)
  • suspicious_html_js<script>, javascript:, on*= in a payload meant to be data

Example:

# Set safe_mode on your MCP URL once, applies to every tool call
mcp-remote https://gateway.pipeworx.io/mcp?safe_mode=annotate

# Or per call via header
curl -X POST https://gateway.pipeworx.io/ \
  -H "X-Pipeworx-Safe-Mode: block" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{...}}'

Performance: <1ms typical, ~5ms on a 50KB response. Bounded at 256KB scan window. Applied universally to every response (pack tools, compound _intel tools, and meta-tools) at a single gate.

False positives are real — annotate mode doesn’t modify the body so wrong scans are recoverable. Use block mode when you’d rather fail closed than ship suspect content to your agent.

Tool-schema version pinning

Every entry in tools/list includes a version field — a short stable hash of {name, description, inputSchema, outputSchema, annotations}. Identical schemas across deploys produce identical versions; any surface-affecting change → new version.

{
  "name": "sec_8k_recent",
  "description": "...",
  "inputSchema": { ... },
  "outputSchema": { ... },
  "annotations": { ... },
  "version": "v1.b8e25a67"
}

Strict MCP clients (Claude Desktop with output-schema validation, ChatGPT MCP) can pin the version on first call and fail-loud if it changes mid-session instead of breaking silently. The pin is content-hash based; examples lists are excluded so editorial regenerations don’t bump the version.

Hash algorithm is djb2 (non-cryptographic) — drift detection, not authentication. If you need crypto-strength pinning, hash the full inputSchema + outputSchema payload yourself.

What about anonymous + BYO tiers?

Allowlist, quotas, and audit-log export all require an OAuth or paid account because the identifier (account ID) survives across requests. Anonymous and BYO identifiers are IP-based and would leak across keys, so the endpoints return 403 with a tier-upgrade message.

safe_mode and tool-schema pinning work on every tier — they’re response-side controls, not auth-gated.

Where to file gaps

If a pattern class you want isn’t covered by safe_mode, or you want per-tool (vs per-pack) allowlisting, file via pipeworx_feedback({type: "feature", message: "..."}). Reviewed daily.

Last reviewed June 1, 2026