rulsynor-core v1.0: An AI Agent You Can Audit
How do you trust an AI Agent in production? Not "trust" as in I think it'll do the right thing. Trust as in cryptographic proof that every decision was correct, traceable, and independently verifiable.
Today: rulsynor-core — open-source Guard engine for AI Agents. MIT license. On npm.
30-Second Demo
bash
npx @openoba/rulsynor-core --tool=exec --cmd="rm -rf /"
🛡️ Decision: DENY
📝 Reason: Destructive command blocked.
🧾 Recorded: sha256:8274b0... (tamper-evident)
🧭 Alternative: Use the read tool to inspect first.
Not just "no." The agent learns why and what to do instead.
What It Does
Guard: Evaluates every tool call before execution — ring-sorted, sub-millisecond. 30 preset rules + write your own.
Audit: Every decision produces a 25-field cryptographic record. JCS + SHA-256. Chain-linked.
Verify: Anyone can verify audit records with zero SDK. Just JCS + SHA-256. No rulsynor needed.
What Makes It Different
Most AI safety tools are prompt-based. "Be careful" isn't governance. rulsynor-core gives you:
7 decision types — ALLOW, DENY, CORRECT, QUARANTINE, REQUEST_HUMAN, NOTIFY, EMERGENCY_HALT
Cryptographic audit chain — not "we logged it," mathematical proof
Third-party verified — 101 cross-implementation vectors, 13/13 passed by Concordia
MIT licensed — zero framework dependencies
Rules in Plain Language
复制
# "If the agent runs rm -rf, block it"
name: block-destructive-rm
when:
conditions:
- field: context.tool.name
operator: eq, value: exec
- field: context.tool.args.command
operator: matches, value: rm\s+-rf
then:
decision: DENY
instruction: Destructive command blocked.
Any LLM can translate English descriptions into ERDL rules. The compiler validates everything before loading.
5-Minute Integration
import { Evaluator, GuardStateManager,
loadPresetRules, toCompiledRules }
from '@openoba/rulsynor-core';
const rules = toCompiledRules(loadPresetRules());
const evaluator = new Evaluator(new GuardStateManager());
const result = evaluator.evaluate(
{ toolName, toolArgs, sessionId, agentId },
rules
);
// result.decision → ALLOW | DENY | CORRECT | ...
Works with LangChain, MCP, custom ReAct loops. Same API everywhere.
Roadmap
30 preset rules, 20 operators
JCS+SHA-256 audit trail
Third-party verified
LangGraph integration guide
Community rule marketplace
GitHub · npm
"LLM vendors deliver exceptional intelligence. We deliver accountability."
Built at OpenOBA. MIT.
Top comments (2)
Really interesting approach. One edge case I’m curious about: how do you represent situations where the engine can’t safely return ALLOW or DENY because required evidence is missing or contradictory? Do you treat that as REQUEST_HUMAN, or model “insufficient evidence” as a distinct outcome?
@brian Jin
Great question — and you've put your finger on a real design tension. Currently ERDL has two mechanisms that partially cover this:
No rule matches → PASS → ALLOW (by default)
When no rule's when matches, the engine returns PASS, falling through to the file-level metadata.decision (default: ALLOW). This is "I had nothing to say" rather than "the evidence is insufficient."
REQUEST_HUMAN — rule author's deliberate choice
A rule can be written to emit REQUEST_HUMAN when key evidence is missing (e.g., when: amount >= 500000 AND risk_score == null). But this conflates two semantically distinct situations: "I see a risk, approve?" vs. "I can't even determine whether there's a risk."
DEFER (v1.2) — the gap you've spotted
The spec already calls out this exact distinction. DEFER means "do not decide now; wait for external signal before evaluating." It differs from REQUEST_HUMAN in that the latter implies a direction (allow/deny) exists but needs human confirmation, while DEFER means the engine genuinely can't form a direction yet.
So to directly answer your question: today the model treats "can't decide" as either PASS→ALLOW (if the rule author didn't anticipate the gap) or REQUEST_HUMAN (if they did, but it's semantically imprecise). DEFER in v1.2 is the deliberate "insufficient evidence" outcome. Good catch — this is worth calling out more explicitly in the current docs as a known design decision rather than an oversight.