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 heldPrefer 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:5100No 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 pausequickstart
The SDK
npm i @phinq/governanceWrap 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_ENFORCEoff — 1 turns on holds; off = shadow (observe only)PHINQ_PORT5100 — Port the proxy listens onPHINQ_UPSTREAMopenrouter.ai — Where OpenAI-style traffic is forwardedPHINQ_ANTHROPIC_UPSTREAMapi.anthropic.com — Upstream for the Messages APIPHINQ_HOLD_TIMEOUT_S240 — Auto-deny if no decision in timePHINQ_TELEGRAM_BOT_TOKEN— — Enable phone approvals (with chat id)PHINQ_CONFIGphinq.yaml — Path to the rules file belowAnd 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 oneToken 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 viewSilence 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 intactThe 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.mdUseful 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