documentation

Run Phinq in ten minutes.

Phinq classifies every action your agent proposes, holds the dangerous ones for your approval, and writes a tamper-evident audit log. Run it as a network proxy or an in-process TypeScript SDK — same decision engine either way.

overview

Two ways to run it

Proxy

Network-level. Point your agent's base URL at it — no agent-code change. Governs anything that speaks the OpenAI, Anthropic, or Gemini APIs (or plain HTTP via the gate), holds high-risk calls for approval, and audits everything.

SDK (TypeScript)

In-process. Import @phinq/governance and gate tool execution directly, before a tool runs. Provider- and gateway-agnostic — it doesn't care what's downstream.

Rule of thumb: want zero agent-code change and network-level enforcement → proxy. Already have a gateway you don't want to disturb, or a TypeScript agent → SDK.

quickstart

The proxy

The fastest path is the wizard — it detects what you run, asks three plain-English questions, and prints the one line to paste:

npx @phinq/phinq     # two-minute interactive setup
phinq start          # run the checkpoint
phinq watch          # live view of anything held

Prefer to run from source? Clone, build, and start — it begins in watch-only mode, inspecting and logging every tool call but holding nothing, so there is zero risk to your agent on day one.

git clone https://github.com/phinq-co/phinq && cd phinq/proxy
npm install && npm run build
npm start            # 127.0.0.1:5100 · watch-only (nothing is held)

Point your agent's model base URL at it. Your existing API key still goes in the request — Phinq forwards it untouched.

# OpenAI-style (Chat Completions incl. streaming / Responses)
base_url = http://127.0.0.1:5100/api/v1

# Anthropic SDK (Messages)
base_url = http://127.0.0.1:5100        # the SDK appends /v1/messages

# Gemini — Gemini CLI / google-genai SDKs
GOOGLE_GEMINI_BASE_URL=http://127.0.0.1:5100

No base URL to change? Anything that can make an HTTP request — an n8n workflow, a Go service, a cron script — gets the same checkpoint with one call. Execute the action only if allowed is true; holds go to your phone first:

POST http://127.0.0.1:5100/phinq/gate
{"name": "delete_records", "arguments": {"ids": [1,2,3]}, "session_key": "my-agent"}
# → {"allowed": false, "resolution": "DENIED", "hold_id": "…"}

Run normally for a while — calls accumulate in phinq-toolcalls.jsonl. See what would have been held, tune until there are no false holds, then enforce:

npm run replay -- phinq-toolcalls.jsonl   # what would have held
PHINQ_ENFORCE=1 npm start                  # now high-risk actions pause

quickstart

The SDK

npm i @phinq/governance

Wrap a tool so every call is gated at the point of execution — a denial returns a message to the agent instead of running the tool:

import { PhinqGovernor } from "@phinq/governance";
import { governTool } from "@phinq/governance/mastra";

const governor = new PhinqGovernor({
  onAudit: (e) => console.log(e),   // optional: never carries raw arguments
});

const safeDelete = governTool(deleteTool, governor, {
  onHold: async (req) => await askOperator(req),  // "approve" | "deny"
});

Or gate any call directly:

const { allowed } = await governor.gate(
  { name: "run_shell", args: { command } },
  { onHold: () => "deny" },   // omit -> fails safe (deny) on HOLD
);
if (allowed) await run();

the risk model

What gets held

Every call is classified deterministically from its name, arguments, and session velocity. ALLOW passes; an IRREVERSIBLE_MEDIUM or IRREVERSIBLE_HIGH class, or any structural trigger, holds.

Held

  • Credential / secret access
  • Billing & payment changes
  • Permission escalation (sudo, chmod, curl | sh)
  • rm -rf, touching .env / keys / certs
  • Bulk deletes (> 5 items)
  • Mass sends (> 3 recipients)
  • Touching Phinq's own config / audit files
  • git force-push
  • Sessions past their token budget (opt-in)

Passes

  • Reads (get / list / search / fetch …)
  • Workspace writes (create / edit / save …)
  • Plain shell with benign arguments
  • A send to a single recipient
  • Risk-reducing actions (undo / rollback / stop …)
  • Unknown tools — flagged, not blocked

Unknown tools default to pass (a zero-false-hold stance, so nothing stalls on day one). You promote real risks during calibration — see configuration. The full rule set lives in classifier.ts.

configuration

Tune it without code

The proxy is configured with environment variables:

PHINQ_ENFORCEoff1 turns on holds; off = shadow (observe only)
PHINQ_PORT5100Port the proxy listens on
PHINQ_UPSTREAMopenrouter.aiWhere OpenAI-style traffic is forwarded
PHINQ_ANTHROPIC_UPSTREAMapi.anthropic.comUpstream for the Messages API
PHINQ_HOLD_TIMEOUT_S240Auto-deny if no decision in time
PHINQ_TELEGRAM_BOT_TOKENEnable phone approvals (with chat id)
PHINQ_CONFIGphinq.yamlPath to the rules file below

And phinq.yaml tunes the rules — thresholds and per-tool risk classes — with no code change:

thresholds:
  external_comm_volume: 3      # hold a send to more than N recipients
  bulk_delete_count: 5         # hold a delete touching more than N items
  session_token_budget: 500000 # tokens per session window; 0/absent = off

tools:
  deploy_to_prod: IRREVERSIBLE_HIGH   # always hold this tool
  send_newsletter: REVERSIBLE         # never hold this one

Token regulation. The proxy reads the usage block of every response and keeps a rolling per-session token count. Set session_token_budget and a session that burns past it is checkpointed — every further tool call holds until you wave it on, so a runaway loop can't quietly spend all night. Spend is written to the audit chain and totalled per model in the oversight report. Off by default.

gateways

Connect your stack

Phinq decides whether an action runs; your gateway decides how the model call is served. They stack — point Phinq's upstream at whatever you use:

OpenRouter — the default. Nothing to set; it already fronts every model.

Anthropic direct PHINQ_ANTHROPIC_UPSTREAM=https://api.anthropic.com.

A gateway (Portkey, Cloudflare, LiteLLM, Vercel AI Gateway) PHINQ_UPSTREAM=https://your-gateway. Its auth/config headers ride through, since your agent sets them.

Don't want to re-route at all — use the SDK. It governs in-process and is gateway-agnostic.

approvals

Approve or deny a hold

When something holds, you decide — from your phone or your terminal:

Telegram

Set PHINQ_TELEGRAM_BOT_TOKEN and PHINQ_TELEGRAM_CHAT_ID. A hold pings your phone with the action and ✅ / ❌ buttons.

CLI

phinq holds          # list pending
phinq approve <id>
phinq deny <id>
phinq watch          # live view

Silence is not approval — an unanswered hold auto-denies after the timeout, and the action never runs.

audit log

Prove what happened

Every governed decision is written to a hash-chained log — each entry seals the one before it, so any edit, reorder, or deletion of history is detectable. Verify the whole chain with one command:

phinq audit verify        # OK — 214 entries, chain intact

The chain records the action, its class, the triggers, and the decision — never the arguments or message content. Tool-call arguments live only in the local corpus file, which the audit chain never touches.

Turn the chain into a human-oversight report — every decision, hold outcome, operator approval, your false-hold rate, and estimated damage prevented, pinned to the log's final hash:

npm run report -- phinq-audit.jsonl --md oversight-report.md

Useful as standing evidence of human oversight — for an EU AI Act Article 14 oversight file, an internal audit, or a customer security review.

Precedent — learn from your verdicts. Every approve/deny is a labeled judgment. phinq learn compiles them into cited policy proposals — unanimous approvals relax a tool, dominant denials pin it at always-hold — and --apply writes the change to phinq.yaml while recording it in the audit chain, so the policy's own history is tamper-evident. Structural triggers are never relaxable; nothing changes without your command.

phinq learn                 # propose policy from your approve/deny history
phinq learn --apply         # write phinq.yaml + record the change in the chain