Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
13 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .env.smoke.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# agent-shield live-smoke configuration — copy to `.env.smoke` and fill in real
# values. `.env.smoke` is gitignored; NEVER commit a real key.
#
# Run the smoke with Node's native env-file loader (no extra deps, no hidden
# magic):
#
# node --env-file=.env.smoke scripts/smoke-live.mjs
#
# WHY A DEDICATED THROWAWAY PROJECT:
# The smoke runs `init` (setupShield), which WRITES policies + an agent into the
# project. There is no test/sandbox key today — every Palveron key is a live
# `pv_live_` key, so isolation comes from pointing at a SEPARATE, DISPOSABLE
# project, not from a key prefix. Create a dedicated throwaway project in the
# dashboard, use its key here, and never point this at a production project.

# Live API key (pv_live_...) of the dedicated throwaway project.
PALVERON_API_KEY=pv_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

# Human-readable name/id of that throwaway project. Required — it makes the
# choice of target conscious (you are naming what you are about to write into).
PALVERON_SMOKE_PROJECT=agent-shield-smoke-throwaway

# Optional — defaults to https://gateway.palveron.com
# PALVERON_API_URL=https://gateway.palveron.com
26 changes: 26 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: CI

on:
push:
branches: [master]
pull_request:
branches: [master]

jobs:
test:
name: test (node ${{ matrix.node }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node: ['18', '20', '22']
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: npm install --no-audit --no-fund
- name: Run tests
run: npm test
12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
node_modules/
package-lock.json
*.tgz
.DS_Store

# Live-smoke config — holds a real pv_live_ key. Never commit.
.env.smoke
.env

# Spawn-diagnostics output (AGENT_SHIELD_DEBUG_LOG_PATH). Sanitized, but may
# carry gateway URLs / agent ids — never commit.
.debug/
315 changes: 315 additions & 0 deletions BEFUND_AGENT_SHIELD_SPAWN.md

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 Palveron A. Podzus

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
223 changes: 187 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@
<a href="https://palveron.com"><img src="https://img.shields.io/badge/Website-palveron.com-0066FF?style=flat-square" alt="Website"></a>
<a href="https://docs.palveron.com"><img src="https://img.shields.io/badge/Docs-docs.palveron.com-0066FF?style=flat-square" alt="Docs"></a>
<img src="https://img.shields.io/badge/Node.js-18+-339933?style=flat-square&logo=node.js&logoColor=white" alt="Node.js">
<img src="https://img.shields.io/badge/License-Proprietary-red?style=flat-square" alt="License">
<img src="https://img.shields.io/badge/License-MIT-green?style=flat-square" alt="License">
</p>

<p align="center">
<a href="#quick-start">Quick Start</a> ·
<a href="#what-it-protects">Protection</a> ·
<a href="#mcp-server">MCP Server</a> ·
<a href="#circuit-breaker">Circuit Breaker</a> ·
<a href="#resilience--fail-policy">Fail Policy</a> ·
<a href="#architecture">Architecture</a>
</p>

Expand All @@ -29,7 +29,19 @@

Your agent runs 24/7. Do you know what it's doing right now?

agent-shield shows you everything your OpenClaw agent does, blocks dangerous actions before they execute, and masks your personal data. **One command. Zero config.**
agent-shield gives your OpenClaw agent a governance check it calls **before** every
high-risk action, records every check, and masks personal data on the way out.
**One command. Zero config.**

> **How enforcement works (the honest version).** agent-shield instructs your agent
> (via its skill + an MCP `governance_check` tool) to check high-risk actions
> *before* running them, and records every check for your dashboard. It is
> **advisory in-path today**: the agent decides to call the check. Hard,
> unbypassable in-path enforcement via an OpenClaw `before_tool_call` hook is on
> the roadmap. What is already hard today: when a check *is* called and returns
> `BLOCK`, the skill tells the agent not to execute — and if our gateway is
> unreachable during a **high-risk** action, agent-shield fails **closed**
> (see [Resilience & Fail Policy](#resilience--fail-policy)).

---

Expand All @@ -42,19 +54,22 @@ npm install -g @palveron/agent-shield
# 2. Set your keys
export PALVERON_API_KEY="your-key" # from dashboard signup
export PALVERON_API_URL="your-api-url" # API endpoint
export OPENAI_API_KEY="sk-..." # your own LLM key (BYOM)

# 3. Initialize
npx agent-shield init
```

That's it. 8 protection rules are now active. Restart your OpenClaw agent.
That's it. Your OpenClaw Shield rule set is now active. Restart your OpenClaw agent.

`init` prints the exact number of rules it activated for your project (it does not
assume a fixed count). Run `agent-shield status` to see them live.

---

## What It Protects

`agent-shield init` activates 8 guardrails automatically — no configuration needed:
`agent-shield init` activates the OpenClaw Shield rule set automatically — no
configuration needed:

| Rule | Detects | Action |
|------|---------|--------|
Expand Down Expand Up @@ -83,67 +98,171 @@ That's the moment you understand what your agent actually does — not because w

## BYOM — Bring Your Own Model

Every OpenClaw user already has an LLM API key. agent-shield's 2-pass system (Regex + AI) uses **your** key for the AI pass. Our LLM cost: zero. Your governance cost: zero on the free tier.
agent-shield's analysis runs **server-side**. Deterministic guardrails (PII, secrets,
shell/destructive patterns) need no LLM at all. For the optional AI analysis pass, the
gateway uses **your** model key — but you configure it **in the dashboard**
(**Settings → Neural Gateway**), where it is stored encrypted and used per project.

> agent-shield does **not** read or forward an LLM API key from your shell. There is
> no `OPENAI_API_KEY` plumbing in the client — the gateway never received it. Set your
> BYOM key once in the dashboard and it applies to every check.

---

## MCP Server

agent-shield includes an MCP (Model Context Protocol) server for integration with coding tools like Cursor and Claude Code:

```bash
# Start as MCP server
npx agent-shield-mcp
agent-shield ships an MCP (Model Context Protocol) server exposing a single
`governance_check` tool that your agent calls before high-risk operations.

`init` wires this into your `openclaw.json` automatically. To configure it manually
(OpenClaw, Cursor, Claude Code), use this exact invocation — `agent-shield-mcp` is a
**bin inside `@palveron/agent-shield`**, not a standalone package:

```json
{
"mcpServers": {
"agent-shield": {
"command": "npx",
"args": ["-y", "-p", "@palveron/agent-shield", "agent-shield-mcp"],
"env": {
"PALVERON_API_URL": "your-api-url",
"PALVERON_API_KEY": "your-key"
}
}
}
}
```

The MCP server exposes a `governance_check` tool that your coding agent calls before executing high-risk operations. Configure it in your `.cursor/mcp.json` or Claude Code settings.
### Which package do I need?

| You want… | Use | Tool(s) |
|-----------|-----|---------|
| OpenClaw zero-config governance + CLI setup | **`@palveron/agent-shield`** (this package) | `governance_check` |
| A generic MCP server for Cursor / Claude Code | [`@palveron/mcp-server`](https://www.npmjs.com/package/@palveron/mcp-server) | `palveron_verify`, `palveron_check_tool_call`, `palveron_list_policies` |

Both talk to the same Palveron Gateway. `agent-shield` is the OpenClaw-focused,
zero-config path; `@palveron/mcp-server` is the general-purpose coding-assistant path.

---

## Circuit Breaker
## Resilience & Fail Policy

agent-shield is built on [`@palveron/sdk`](https://www.npmjs.com/package/@palveron/sdk),
which provides request retries and a circuit breaker. On top of that, agent-shield
applies a **tiered fail policy** so an outage on our side never silently disables your
governance:

agent-shield implements a 3-state circuit breaker to ensure your agent **never stops** because of a governance outage:
| Situation | Behavior |
|-----------|----------|
| Gateway returns a verdict | The real decision is used (`ALLOW` / `BLOCK` / `MODIFY` / `APPROVAL`) |
| **Contract / auth error** (bad request, invalid key) | **Fail LOUD** — the error is surfaced; **never** a silent `ALLOW` |
| Gateway unreachable, **HIGH-risk** action (shell, exec, delete, `git_push`, destructive, secret-exfil) | **Fail CLOSED** — `BLOCK`. We do not let a dangerous action run unchecked during our downtime |
| Gateway unreachable, MEDIUM-risk action (incl. any unknown tool) | **Fail OPEN** — `ALLOW`, so a transient outage never blocks ordinary work |

| State | Behavior |
|-------|----------|
| **Closed** | Normal operation — every call goes to the Palveron API |
| **Open** | After 3 consecutive failures — returns `ALLOW` immediately, agent keeps running |
| **Half-Open** | After 30s — sends one probe request. Success → Closed. Failure → Open again |
Set `AGENT_SHIELD_FAIL_CLOSED=true` to fail closed for **all** risk levels when the
gateway is unreachable (maximum safety; availability traded away).

**Fail-open by design.** If the Palveron gateway is unreachable, your agent continues with `{ decision: "ALLOW", reason: "circuit_open" }`. We never block your agent because of our downtime.
> This is a deliberate change from a blanket "always fail open" stance. For the
> dangerous class of actions, security beats availability: if we can't check it, we
> don't run it.

---

## CLI Commands

```bash
agent-shield init # Initialize shield, activate 8 rules, register agent
agent-shield status # Show connection status, active rules, circuit state
agent-shield test # Send a test prompt through the governance pipeline
agent-shield init # Initialize shield, activate rules, register agent
agent-shield status # Show connection status, active rules, 24h stats
agent-shield test # Send test prompts through the governance pipeline
agent-shield --help # Show all commands
```

---

## Troubleshooting — spawn diagnostics

When agent-shield runs **as a spawned MCP subprocess** (OpenClaw, Cursor, Claude
Code, …), the host swallows its stderr, so a normal log is invisible. For cases
where a governed call behaves differently under the host than when invoked
directly, set `AGENT_SHIELD_DEBUG_LOG_PATH` to a file path and agent-shield
appends an ordered, append-only JSONL event log (process start + env snapshot,
each MCP message, every gateway call with its outcome, circuit-breaker
transitions, and the fail-closed branch):

```bash
AGENT_SHIELD_DEBUG_LOG_PATH=./.debug/spawn.jsonl node bin/agent-shield-mcp.mjs
```

- **Off by default** — unset means zero file IO and no behavioral difference.
- **Never affects governance** — diagnostics can't throw; on any IO error they
go silent. Correctness beats diagnosis.
- **Secret-safe** — the API key appears only as length/presence, tool-call
arguments are never logged (only their key names + byte size), and proxy URLs
are reduced to `host:port`.

Add the same env var to the host's MCP registration to capture a real spawn run,
then compare it against a direct run pointed at the same log file. The `.debug/`
folder is gitignored.

### Antivirus / firewall HTTPS inspection (TLS-untrusted gateway)

**Symptom:** every `governance_check` returns `BLOCK` with reason
`gateway_tls_untrusted` (or, with an older SDK, an opaque
`gateway_unavailable_failclosed`) — but **only** when agent-shield runs as a
spawned subprocess (e.g. inside OpenClaw), while a direct run works.

**Cause:** a TLS-intercepting security suite (Norton, McAfee, Zscaler, corporate
proxies, …) re-signs HTTPS traffic with its **own root CA**. That CA lives in the
Windows/macOS system trust store but **not** in Node's bundled CA set, so Node
rejects the certificate (`UNABLE_TO_VERIFY_LEAF_SIGNATURE`). The direct shell run
is often not inspected; the spawned child is.

**Fix — trust the OS certificate store (Node ≥ 22):**

```bash
node --use-system-ca bin/agent-shield-mcp.mjs
```

In an MCP host registration, use `command: node` with `--use-system-ca` as the
first argument before the script path.

**Node < 22 fallback:** point Node at the inspecting CA explicitly:

```bash
NODE_EXTRA_CA_CERTS=/path/to/your-av-or-proxy-root-ca.pem node bin/agent-shield-mcp.mjs
```

> **Never** set `NODE_TLS_REJECT_UNAUTHORIZED=0`. That disables certificate
> verification entirely and has no place in a security product. `--use-system-ca`
> trusts the legitimate OS-store CA **without** weakening verification.

---

## Architecture

agent-shield is a **thin client**. It contains:

- HTTP client with retry logic and circuit breaker
- A small facade over [`@palveron/sdk`](https://www.npmjs.com/package/@palveron/sdk)
(which owns the verify contract, retries, and circuit breaker)
- The tiered fail policy and OpenClaw Shield setup/status calls
- CLI for initialization and status checks
- MCP server entry point for coding tool integration
- MCP server entry point for agent / coding-tool integration
- Local tool-risk classification (trivial mapping, no IP)

**What it does NOT contain:** No PII patterns, no policy evaluation engine, no guardrail logic. All intelligence lives server-side in the [Palveron Gateway](https://github.com/palveron/gateway). This protects our IP and keeps the client small and dependency-free.
**What it does NOT contain:** No PII patterns, no policy evaluation engine, no guardrail
logic. All intelligence lives server-side in the
[Palveron Gateway](https://github.com/palveron/gateway). This keeps the client small and
its single dependency (`@palveron/sdk`) is the one source of truth for the API contract —
no second client implementation to drift from the server.

```
Your Agent ──→ agent-shield (HTTP client) ──→ Palveron Gateway
│ │
Circuit Breaker │ 8 Guardrails
Fail-Open on timeout │ PII Detection
│ │ Blockchain Proof
▼ ▼
Agent continues Trace in Dashboard
Your Agent ──→ agent-shield ──→ @palveron/sdk ──→ Palveron Gateway
Tiered fail policy Guardrails
(HIGH → fail-closed) │ PII Detection
│ Blockchain Proof
Agent decision Trace in Dashboard
```

---
Expand All @@ -154,9 +273,40 @@ Your Agent ──→ agent-shield (HTTP client) ──→ Palveron Gateway
|----------|:--------:|-------------|
| `PALVERON_API_KEY` | ✅ | Your project API key (from dashboard) |
| `PALVERON_API_URL` | ✅ | Gateway API endpoint |
| `OPENAI_API_KEY` | — | Your LLM key for AI-pass (BYOM) |
| `AGENT_SHIELD_FAIL_CLOSED` | — | `true` forces fail-closed for all risk levels when the gateway is unreachable. Default: tiered |

Legacy fallback: `AGENT_SHIELD_API_KEY` / `AGENT_SHIELD_API_URL` are also accepted.
BYOM model keys are configured in the dashboard (**Settings → Neural Gateway**), not here.

---

## Live smoke (`scripts/smoke-live.mjs`)

An end-to-end smoke that runs the eight launch checks against a real gateway. It
is **not** shipped in the npm package.

> ⚠️ **Run it only against a dedicated, disposable project.** Check #8 (`init` /
> `setupShield`) **writes policies and an agent** into the project. There is no
> test or sandbox key today — every Palveron key is a live `pv_live_` key, so
> isolation comes from targeting a **separate throwaway project**, not from a key
> prefix.

Setup:

1. Create a dedicated throwaway project in the dashboard (e.g. named
`agent-shield-smoke-throwaway`).
2. `cp .env.smoke.example .env.smoke` and fill in that project's `pv_live_` key
and name. `.env.smoke` is gitignored.
3. Run it:

```bash
node --env-file=.env.smoke scripts/smoke-live.mjs
```

The script refuses to run unless both `PALVERON_API_KEY` and
`PALVERON_SMOKE_PROJECT` are set — naming the throwaway project is the conscious
confirmation that you are pointing at a disposable target. Exit code `0` means
all hard checks passed.

---

Expand Down Expand Up @@ -184,4 +334,5 @@ Legacy fallback: `AGENT_SHIELD_API_KEY` / `AGENT_SHIELD_API_URL` are also accept

## License

Proprietary — © 2026 Palveron A. Podzus. All rights reserved.
MIT — © 2026 Palveron A. Podzus. This thin client is open source. The governance
engine (the Palveron Gateway) is proprietary.
Loading
Loading