Documentation
Everything you need to run SafeNode in production, call the evaluate API, and wire agents such as Seedling or Operator.
Getting started
In a few minutes you will: create an account, set up an organization and an agent, create an API key, and call POST /api/v1/evaluate so your software can ask “is this action allowed?” before it runs.
Use the sidebar links to jump between sections. When you are logged into the dashboard, open Documentation from the Help block in the sidebar for the same guide.
What is SafeNode?
SafeNode is a policy firewall for AI agents. Before your agent performs an action (send email, run a shell command, call an external API, write a file, etc.), you send a structured description of that action. SafeNode runs it through your organization’s policies and returns a decision: allow, warn, review, or deny. Your integration chooses how strictly to enforce each outcome.
Why use it? You get one place for rules, a full audit trail of evaluations, and the ability to tighten or loosen behavior without redeploying every client.
Core concepts
- Organization
- Top-level tenant. Policies, agents, API keys, and evaluations belong to one organization.
- Agent
- A logical actor (e.g. “Support bot”, “Seedling”, “Operator”). API keys are tied to an agent so you can filter and audit by source.
- API key
- Secret used in
Authorization: Bearer …(orX-Api-Key). Identifies the agent and organization for each evaluate call. action_type- Short string naming the kind of action (e.g.
tool.shell.exec,send_email). Your policies can match on this. payload- JSON object with action-specific details (arguments, paths, recipients, model name, etc.).
context- JSON object with environment metadata (region, estimated cost, sensitivity, workspace id). Helps scoring and rules.
trace_id- Returned on every successful evaluation. Use it to correlate logs with a row in the Decision Feed.
Create your account
You need a SafeNode account to create organizations, agents, and API keys.
Go to the sign‑up page
Open Register (or “Register” in the site header).
Enter your email and password
Use a real email and a strong password.
Log in
After registering, log in via Log in when needed.
Create an organization
Everything in SafeNode lives under an organization. You can have more than one (e.g. staging vs production).
Open the dashboard
Click Dashboard in the header (or go to /admin).
Create or choose an organization
Create one with a name and slug, or use the organization switcher in the sidebar.
Remember the slug
The slug (e.g. acme) is a short identifier you may reference in context from your apps.
Add an agent
An agent is one thing that can perform actions. Create separate agents for Seedling, Operator, or each production service so evaluations stay attributable.
Go to Agents
In the dashboard sidebar, open Agents.
Create a new agent
Click “New agent”, set a name (e.g. “Seedling” or “Operator”) and optional slug, then save.
One agent per logical actor
Prefer one agent per integration or environment rather than sharing one key everywhere.
Get your API key
Programs authenticate with an API key tied to an agent. The full key is shown only once when created—store it in a secret manager or environment variable.
Open API keys
In the sidebar, go to API keys (under the agent or organization, depending on your panel layout).
Create a key
Link it to the agent you created. Copy the key immediately.
Send it on every evaluate request
Use Authorization: Bearer YOUR_KEY or X-Api-Key: YOUR_KEY. Missing or invalid keys receive 401.
Call the API
Main endpoint: POST /api/v1/evaluate. Full URL is your app base URL plus /api/v1/evaluate (from APP_URL), e.g. https://safenode.tech/api/v1/evaluate.
Headers
POST /api/v1/evaluate
Content-Type: application/json
Authorization: Bearer YOUR_API_KEY
Body (required fields)
action_type(string, required)payload(object, optional—omit or send{})context(object, optional)agent_id(string, optional—usually omitted when the API key already identifies one agent)
Minimal example
{
"action_type": "send_email",
"payload": {
"to": "user@example.com",
"subject": "Your order",
"body": "Order #123 has shipped."
}
}
With context (recommended)
{
"action_type": "call_model",
"payload": { "model": "gpt-4", "tokens": 500 },
"context": {
"region": "eu-west-1",
"cost_usd": 0.02,
"sensitivity": "internal"
}
}
Align context field names with what your policies expect.
Understand the response
On success, JSON includes:
- decision —
allow,warn,review, ordeny - trace_id — unique id for this evaluation
- impact_score / risk_score — numeric scores from policy scoring
- matched_policies — rules that matched
- reasons — human-readable strings
- alternatives — suggested safer options when applicable
Suggested enforcement:
deny— do not perform the action.review— block until a human approves (or your product policy allows auto-escalation).warn— allow only if your product policy says warnings are acceptable; log and surface to the user.allow— proceed.
{
"decision": "allow",
"trace_id": "ev_abc123",
"impact_score": 0.2,
"risk_score": 0.1,
"matched_policies": [],
"reasons": [],
"alternatives": []
}
Rate limits & errors
The evaluate endpoint is rate limited per API key (and organization) to protect the service. Defaults are configurable on the server; typical values are on the order of tens to low hundreds of requests per minute per key. If you exceed the limit, you may receive 429 Too Many Requests.
Other common responses:
- 401 — missing/invalid API key or expired key.
- 422 — request validation failed or JSON-encoded
payload/contextexceeds the configured maximum size. - 500 — server error (rare); check service status and logs.
Implement retries with backoff for 429 and transient 5xx. For high-risk actions, prefer fail-closed if SafeNode is unreachable unless you explicitly allow read-only fail-open behavior.
Using the dashboard
- Decision Feed — recent evaluations; filter by decision, agent, or time; open a row for full detail and
trace_id. - Stats — volume and outcome breakdowns.
- Policies — active policy version, hard rules, soft rules, weights, and decision bands.
- Overrides — manual allow/deny on past evaluations (audited).
For a short marketing overview of the product, you can still read How it works on the homepage—this documentation is the operational source of truth.
Seedling integration (walkthrough)
Seedling here means an IDE- or assistant-style integration that can invoke tools (edit files, run terminal commands, fetch URLs, etc.) on the user’s machine. SafeNode does not run inside the IDE; your Seedling bridge calls SafeNode over HTTPS before executing a tool.
Create a dedicated agent
In the dashboard, create an agent named e.g. “Seedling” (or per-workspace if you want separate audit trails).
Create an API key
Store SAFENODE_API_KEY (and SAFENODE_BASE_URL) in the environment where the bridge runs—not in public repos.
Hook before each tool execution
When Seedling is about to run a tool, build:
action_type— stable string, e.g.seedling.tool.file_write,seedling.tool.terminal,seedling.tool.fetch(use a naming convention your policies can target).payload— tool name, normalized arguments (paths, command, URL, etc.). Avoid secrets in payload when possible.context— workspace id, repo name, user id, OS, “read_only”: true/false, estimated risk.
POST /api/v1/evaluate
Send the JSON body with Bearer auth. Record trace_id in your local log line for that tool call.
Enforce the decision
Map deny → cancel tool. review → show approval UI. warn → proceed only if your UX allows. allow → run the tool.
If the network call fails, recommended default for mutating tools is do not run the tool. Optional: allow read-only tools when explicitly configured.
Operator integration (walkthrough)
Operator is a local AI system that should use the same pattern as Seedling: evaluate first, act second.
Agent + API key
Create an agent “Operator” (or per-installation). Issue an API key; ship it via environment variable or secure first-run setup—not baked into binaries.
Centralize gating
Wrap every high-risk capability (shell, network, filesystem writes, plugin load, etc.) with one function that calls evaluate and returns allow/deny.
Use consistent action_type names
E.g. operator.shell.exec, operator.fs.write, operator.http.request. Policies can then target Operator without affecting other agents.
Ship the SDK (optional)
Use the JavaScript SDK in this repository (see below) for timeouts, retries, and conservative defaults.
Environment variables for Operator:
SAFENODE_BASE_URL— e.g.https://safenode.techSAFENODE_API_KEY— agent API key
Operator SDK
The SafeNode Operator SDK is a small ES module in this repo (no npm package required to start):
- Path:
scripts/safenode-operator-sdk.mjs - Exports:
SafeNodeClient,gateWithSafeNode - Features: Bearer auth, request timeout, exponential backoff retries on transient failures,
evaluate()andgateAction()with fail-closed defaults and optional read-only fail-open.
import { SafeNodeClient } from "./scripts/safenode-operator-sdk.mjs";
const client = new SafeNodeClient({
baseUrl: process.env.SAFENODE_BASE_URL,
apiKey: process.env.SAFENODE_API_KEY,
timeoutMs: 5000,
maxRetries: 2,
});
const gate = await client.gateAction(
{
actionType: "operator.shell.exec",
payload: { command: "npm install" },
context: { environment: "local", sensitivity: "internal" },
},
{ allowWarn: true, allowReview: false, failOpenForReadOnly: true, isReadOnlyAction: false }
);
if (!gate.allowed) {
// block action; gate.reason, gate.traceId, gate.response
}
Copy the file into your Operator repo or depend on the SafeNode monorepo path that matches your distribution model.
Full example
- Register and create organization + agent + API key.
- Set
SAFENODE_API_KEYin your environment. - Run:
curl -X POST https://safenode.tech/api/v1/evaluate \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $SAFENODE_API_KEY" \
-d '{
"action_type": "send_email",
"payload": { "to": "user@example.com", "subject": "Hi", "body": "Hello" },
"context": { "region": "us-east-1" }
}'
Inspect decision and trace_id, then find the evaluation in the Decision Feed.
Site admin (production)
Site admins can access the separate Super Admin panel at /super-admin (manage users, site-wide settings). This is independent of organization owner / admin roles inside the main dashboard.
On the server, SSH to the app directory and run:
php artisan safenode:make-site-admin your@email.com
If the user does not exist yet:
php artisan safenode:make-site-admin your@email.com --create
The command will prompt for name, password, and optionally an organization slug. Afterward that user can open /super-admin when logged in.