Veefore docs

Automatic setup

What `veefore` finds in your project, what it creates, how it changes your code, and how to do any part of it by hand instead.

veefore, run in an agent project with no command, does the whole integration: sign in, scan, set up, change the code, verify. Each step shows what it is about to do and asks before doing it. Each is also a command of its own, so you can run, skip or repeat any part.

Step Command on its own Changes anything?
Sign in through the browser veefore login Stores a session token
Scan the project veefore scan No
Create agent, tools, policy veefore init In Veefore, after you confirm
Wrap tools in guard() veefore integrate Your files, after you confirm
Verify veefore integrate --check No
Reverse the code change veefore undo Restores your files
Take Veefore out entirely veefore remove Your files, then disconnects the project

#The scan

Static and local. The project's code is read, never run, and nothing is sent anywhere.

Frameworks recognised: Vercel AI SDK, OpenAI function tools, OpenAI Agents SDK (TypeScript and Python), LangChain.js and LangChain, CrewAI, and MCP servers in both languages.

No framework: tools written as plain data, the way you hand them to a model yourself: an object or dict with name, description and an input schema (parameters, input_schema or inputSchema), in TypeScript, JavaScript or Python. All three keys must be on the same object, so a config block that only has a name is not mistaken for a tool.

For each tool it reports:

  • The external hosts it calls, from fetch, axios, requests, httpx and base-URL assignments. LLM provider endpoints are recognised as the model's and left out. These become the tool's destination allowlist.
  • The credentials it reads, by environment variable name only. Model keys such as OPENAI_API_KEY are listed separately and never touched.
  • A risk level, from the tool's name. A high-impact word anywhere (refund, delete, transfer, deploy, …) makes it HIGH and dangerous. Otherwise the leading verb decides: lookupOrder reads, placeOrder writes. Unknown intent is MEDIUM, never LOW. A risk the definition states itself (risk: 'critical') can raise the rating but never lower it.

What it will not do: open .env or any other hidden file, read a secret value, or descend into node_modules, virtualenvs, build output or tests.

bash
veefore scan          # what setup would find, without signing in
veefore scan --json   # the scan and the plan it implies, for scripts

Detection is pattern-based and can miss an unusual definition. Anything it misses can be registered in the console under Tools.

#What setup creates

After one confirmation, through the same public API the console uses:

  1. A capability per tool, tool.<key>, so authorizing one tool grants nothing else. Existing ones are reused.
  2. The agent, with an Ed25519 key pair generated on your machine. Only the public key is sent; the private key goes to .env.local and nowhere else.
  3. Each tool, registered and then authorized for this agent only. Registration on its own grants nothing.
  4. A starter policy from the safe-defaults template, bound to the workspace, with the environment in OBSERVE. Tools flagged dangerous require approval under it once you enforce.

Running it again in another project reuses capabilities and tools that already exist rather than duplicating them.

#Client-executed tools

Setup registers tools as kind CLIENT: Veefore decides, your code executes. Every call is authenticated, checked against policy, risk and approval, and recorded; when it is permitted, guard() runs your function.

The credential stays in your code, so this is a control only while calls go through guard(). A CLIENT tool cannot be given an endpoint or a brokered credential, so it never claims more protection than it has.

To close that gap, broker the credential and re-register the tool as HTTP. Veefore then performs the call itself with a key your agent never holds, and guard() returns that result without running your function, so nothing executes twice.

#The code change

veefore integrate (offered automatically at the end of setup) wraps the function that performs each registered tool.

diff
+import { guard } from '@veefore/sdk';

 export const tools = {
   lookupOrder: tool({
     description: 'Look up an order',
-    execute: async ({ orderId }) => {
+    execute: guard('lookup_order', async ({ orderId }) => {
       return orders.get(orderId);
-    },
+    }),
   }),
 };
diff
 from langchain_core.tools import tool
+from veefore import guard

 @tool
+@guard("lookup_invoice")
 def lookup_invoice(invoice_id: str) -> dict:
     """Fetch an invoice by id."""

@guard goes directly above def, below the framework's decorator. It uses functools.wraps, so @tool, @function_tool and @mcp.tool() still see the original name, signature, type hints and docstring.

The rules the rewrite follows:

  • Only registered tools are touched, and only the function is wrapped. Your function bodies are never edited.
  • JavaScript/TypeScript: only async functions. guard() returns a Promise, so wrapping a synchronous function would change what it returns.
  • It never guesses. A tool it cannot place with certainty is listed with the exact change to make (see below).
  • Re-running changes nothing. Already-guarded tools are left alone. If your file already uses the name guard, the import is aliased.

#Safety

  1. You see a unified diff of every file and are asked once.
  2. A baseline typecheck runs first, so errors your project already had are not blamed on the change.
  3. Every changed file is backed up to .veefore/backups/.
  4. Then your own checks run: the project's TypeScript compiler (--noEmit), node --check for plain JavaScript, or a Python syntax parse. If one newly fails, every file is restored automatically.
bash
veefore integrate --dry-run   # show the diff, write nothing
veefore undo                  # restore the last change

veefore undo leaves alone any file you edited after the change and lists it; --force restores it anyway, losing your later edits.

#Tools defined as data

When tools are plain definitions, no one function performs each tool: your code runs whichever tool the model picked, usually in one dispatch function. One guard there covers every tool routed through it.

veefore integrate finds that place and adds the guard itself when the shape is unambiguous: a function named like executeTool, runTool or handle_tool_call, whose argument carries the tool's name (call.tool, call.name), with exactly one awaited call inside that receives it.

diff
+import { guard } from '@veefore/sdk';
 …
 export async function executeTool(call: ToolCall): Promise<ToolResult> {
   …
-  result = await executor.execute(call);
+  result = await guard(call.tool, (_args: typeof call.params) => executor.execute(call))(call.params);

It goes through the same diff, backup, typecheck and rollback as every other change. In a monorepo, each changed file is typechecked by its own package's tsconfig.json.

When the place is found but the shape is not certain, or the project is Python, you get the same three choices as below: make the change by hand from exact instructions naming the file and function, or have AI make it with your own key or with Veefore AI. An AI change here is accepted only if it adds a single guard keyed by the tool name and nothing else.

ts
import { guard } from '@veefore/sdk';

// before:  const result = await runTool(call);
const result = await guard(call.name, (_args: typeof call.args) => runTool(call))(call.args);
python
from veefore import guard

# before:  result = run_tool(call.name, call.args)
result = guard(call.name, describe=lambda _args: {"parameters": _args})(
    lambda _args: run_tool(call.name, call.args))(call.args)

The first argument must be the tool's Veefore key, which is its name when that is lower_snake_case. A refusal throws, so the guard belongs where your code already turns errors into a tool result for the model.

veefore integrate --check then reports those tools as guarded at the dispatch point. It checks that the guard exists, not that every path reaches it, so keep it where every tool call passes.

#Tools the rewrite will not change

A method-style execute, a synchronous function, an OpenAI tool that is only a schema, or LangChain Tool(func=…). For these you choose:

text
How would you like to handle them?
  1  I will change them myself (show me exactly what to change)
  2  Use AI with my own Anthropic or OpenAI key (code goes only to that provider)
  3  Use Veefore AI (code is sent to Veefore, then to its model provider)
  4  Skip for now

Or choose up front with --assist=manual, --assist=own, --assist=veefore or --assist=off. Without a terminal to ask, the default is manual.

#By hand

Nothing is sent anywhere. For each tool you get the file, the line, the import to add and the exact code to change it to:

text
src/agent.ts
  Add once, with the other imports:
    import { guard } from '@veefore/sdk';

  lookup_order  near line 8 — execute is written as a method
    // before:  async execute(args) { … }
    execute: guard('lookup_order', async (args) => { … }),

When you are done:

bash
veefore integrate --check

It lists every registered tool as guarded or not, and exits 1 if any are not, so it also works as a CI check.

#With AI

Both AI options work the same way:

  • Secrets are replaced with placeholders before anything leaves your machine, and restored locally afterwards: provider keys (Stripe, OpenAI, Anthropic, AWS, GitHub, Slack, Google), JWTs, passwords inside URLs, private keys, and random-looking string literals.
  • You see what is sent and to whom, and must agree: which files, the destination, the model, and how many secrets were removed. Only the files that need the change are sent.
  • The reply is untrusted. It is accepted only if it keeps every token of your file, adds no new string, adds almost nothing beyond the guard() change and actually guards the requested tools. A rejected or declined reply falls back to the manual instructions.
  • An accepted change is marked "proposed by AI" in the diff and goes through the same confirmation, backup, checks and rollback as everything else.

Your own key. Uses ANTHROPIC_API_KEY or OPENAI_API_KEY from your shell, or a key you paste, held in memory for that run only. It lists the models your key can use and you pick one (VEEFORE_AI_MODEL preselects). Your code goes from your machine straight to that provider, never through Veefore.

bash
ANTHROPIC_API_KEY=… veefore integrate --assist=own

Veefore AI. Available when it is enabled for your Veefore, with the models its operator chose. Your code goes to Veefore and then to its model provider. It is used for that request only and never stored or logged; the audit trail records who asked, which model, which tools and the file size. Rate limited per person.

#Removing Veefore from a project

bash
veefore remove --dry-run   # show what would change
veefore remove

It offers to revoke the agent if it is still active, takes every guard() back out of the code, and then disconnects the project by deleting veefore.config.json and the Veefore block in .env.local, including the private key. The code change gets the same diff, confirmation, backup, checks and rollback as adding it. Unlike veefore undo, it rewrites the code instead of restoring an old copy, so edits you made after setup are kept.

Revoked in the console? The console cannot change files on a developer's machine, so revoking an agent there stops it at once, and every guarded call is refused from then on, but leaves the code as it is. The next time anyone runs veefore in that project, it notices and offers two ways forward: remove Veefore from the code, or set the project up again with a new agent. The guarded code keeps working with the new agent, because the tool keys are the same. veefore integrate refuses to add guards for a revoked agent and points at the same two options.

#Running veefore again

In a project that is already connected, veefore shows the agent, the environment and the agent's status in Veefore, then offers: check status, protect tools in the code, re-run setup, remove Veefore from the project, or sign out.

Identity, authority, policy and enforcement for autonomous AI agents.