Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
34 changes: 26 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# openclaw-predicate-provider
# predicate-claw

> **Stop prompt injection before it executes.**

Expand All @@ -17,8 +17,8 @@ Policy: DENY (sensitive_path + untrusted_source)
Result: ActionDeniedError — SSH key never read
```

[![npm version](https://img.shields.io/npm/v/openclaw-predicate-provider.svg)](https://www.npmjs.com/package/openclaw-predicate-provider)
[![CI](https://github.com/PredicateSystems/openclaw-predicate-provider/actions/workflows/tests.yml/badge.svg)](https://github.com/PredicateSystems/openclaw-predicate-provider/actions)
[![npm version](https://img.shields.io/npm/v/predicate-claw.svg)](https://www.npmjs.com/package/predicate-claw)
[![CI](https://github.com/PredicateSystems/predicate-claw/actions/workflows/tests.yml/badge.svg)](https://github.com/PredicateSystems/predicate-claw/actions)
[![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](LICENSE)

---
Expand All @@ -45,6 +45,8 @@ a document, or a webpage can hijack your agent.

Predicate Authority intercepts every tool call and authorizes it **before execution**.

*Identity providers give your agent a passport. Predicate gives it a work visa.* We don't just know who the agent is; we cryptographically verify exactly what it is allowed to do, right when it tries to do it.

| Without Protection | With Predicate Authority |
|-------------------|-------------------------|
| Agent reads ~/.ssh/id_rsa | **BLOCKED** - sensitive path |
Expand All @@ -57,21 +59,37 @@ Predicate Authority intercepts every tool call and authorizes it **before execut
- 🔒 **Deterministic** — No probabilistic filtering, reproducible decisions
- 🚫 **Fail-closed** — Errors block execution, never allow
- 📋 **Auditable** — Every decision logged with full context
- 🛡️ **Zero-egress** — Sidecar runs locally; no data leaves your infrastructure

---

## Quick Start

### 0. Prerequisites

This SDK requires the Predicate Authority sidecar to evaluate policies locally.

```bash
# macOS (Homebrew)
brew install predicatesystems/tap/predicate-authorityd

# Or via install script
curl -sSL https://predicate.systems/install.sh | bash

# Or Docker
docker run -d -p 8787:8787 predicatesystems/authorityd:latest
```

### 1. Install

```bash
npm install openclaw-predicate-provider
npm install predicate-claw
```

### 2. Protect your agent

```typescript
import { GuardedProvider } from "openclaw-predicate-provider";
import { GuardedProvider } from "predicate-claw";

const provider = new GuardedProvider({
principal: "agent:my-agent",
Expand All @@ -92,8 +110,8 @@ const content = await fs.readFile(path); // Only runs if authorized
### 3. See it in action

```bash
git clone https://github.com/PredicateSystems/openclaw-predicate-provider
cd openclaw-predicate-provider
git clone https://github.com/PredicateSystems/predicate-claw
cd predicate-claw
npm install
npm run test:demo
```
Expand Down Expand Up @@ -386,5 +404,5 @@ MIT OR Apache-2.0

<p align="center">
<strong>Don't let prompt injection own your agent.</strong><br>
<code>npm install openclaw-predicate-provider</code>
<code>npm install predicate-claw</code>
</p>
50 changes: 50 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,56 @@ sidecar runs.
3. Show deny result and user-facing blocked message.
4. Show test command and green output as reproducible evidence.

## Non-Web Evidence Provider Demo

Demonstrates terminal and desktop accessibility evidence providers with canonical
hashing for reproducible `state_hash` computation.

### Run the Demo

```bash
npx tsx examples/non-web-evidence-demo.ts
```

### What It Shows

1. **Terminal Evidence** - Captures command-line state with:
- Path normalization (`/workspace/./src/../src` → `/workspace/src`)
- Whitespace collapsing (`git status` → `git status`)
- ANSI code stripping (removes color codes)
- Timestamp normalization (`[12:34:56]` → `[TIMESTAMP]`)
- Secret redaction (environment variables like `AWS_SECRET_KEY`)

2. **Desktop Evidence** - Captures accessibility tree state with:
- App name normalization
- UI tree text normalization
- Whitespace handling

3. **Hash Stability** - Proves that minor variations produce identical hashes
when canonicalization is enabled.

### API Usage

```typescript
import {
OpenClawTerminalEvidenceProvider,
buildTerminalEvidenceFromProvider,
} from "predicate-claw";

const provider = new OpenClawTerminalEvidenceProvider(() => ({
sessionId: "my-session",
cwd: process.cwd(),
command: "npm test",
transcript: "...",
}));

const evidence = await buildTerminalEvidenceFromProvider(provider, {
useCanonicalHash: true, // default
});

console.log(evidence.state_hash); // sha256:...
```

## Other Examples

- `openclaw_integration_example.py` - Python integration example
Expand Down
184 changes: 184 additions & 0 deletions examples/non-web-evidence-demo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/**
* Non-Web Evidence Provider Demo
*
* This example demonstrates how to use the terminal and desktop accessibility
* evidence providers with canonical hashing for reproducible state_hash computation.
*
* The canonicalization ensures that minor variations (ANSI codes, timestamps,
* whitespace) don't break hash verification.
*
* Run with: npx tsx examples/non-web-evidence-demo.ts
*/

import {
OpenClawTerminalEvidenceProvider,
OpenClawDesktopAccessibilityEvidenceProvider,
buildTerminalEvidenceFromProvider,
buildDesktopEvidenceFromProvider,
type TerminalRuntimeContext,
type DesktopRuntimeContext,
} from "../src/index.js";

// ============================================================================
// Terminal Evidence Demo
// ============================================================================

async function demoTerminalEvidence(): Promise<void> {
console.log("=".repeat(60));
console.log("Terminal Evidence Provider Demo");
console.log("=".repeat(60));

// Simulated terminal runtime context (in real usage, capture from actual terminal)
const terminalContext: TerminalRuntimeContext = {
sessionId: "demo-session-001",
terminalId: "term-1",
cwd: "/workspace/./src/../src", // Will be normalized to /workspace/src
command: "git status ", // Extra whitespace will be collapsed
// Transcript with ANSI codes and timestamps that will be normalized
transcript: `\x1b[32m[12:34:56]\x1b[0m Running git status...
On branch main
Your branch is up to date with 'origin/main'.

\x1b[33mnothing to commit, working tree clean\x1b[0m`,
env: {
HOME: "/home/user",
PATH: "/usr/bin:/bin",
AWS_SECRET_KEY: "should-be-redacted", // Secrets are redacted
EDITOR: "vim",
},
};

// Create provider with capture function
const terminalProvider = new OpenClawTerminalEvidenceProvider(
() => terminalContext,
);

// Build evidence with canonical hashing (default)
const terminalEvidence = await buildTerminalEvidenceFromProvider(
terminalProvider,
{ useCanonicalHash: true },
);

console.log("\nTerminal Evidence:");
console.log(JSON.stringify(terminalEvidence, null, 2));

// Demonstrate hash stability - same content with different formatting
const terminalContext2: TerminalRuntimeContext = {
...terminalContext,
cwd: "/workspace/src", // Same path after normalization
command: "git status", // Same command after whitespace collapse
// Same content without ANSI codes and different timestamps
transcript: `[09:00:00] Running git status...
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean`,
};

const terminalProvider2 = new OpenClawTerminalEvidenceProvider(
() => terminalContext2,
);

const terminalEvidence2 = await buildTerminalEvidenceFromProvider(
terminalProvider2,
{ useCanonicalHash: true },
);

console.log("\nTerminal Evidence (normalized variant):");
console.log(JSON.stringify(terminalEvidence2, null, 2));

const hashesMatch = terminalEvidence.state_hash === terminalEvidence2.state_hash;
console.log(`\nHashes match: ${hashesMatch ? "YES (canonicalization working)" : "NO"}`);
}

// ============================================================================
// Desktop Accessibility Evidence Demo
// ============================================================================

async function demoDesktopEvidence(): Promise<void> {
console.log("\n" + "=".repeat(60));
console.log("Desktop Accessibility Evidence Provider Demo");
console.log("=".repeat(60));

// Simulated desktop accessibility context
const desktopContext: DesktopRuntimeContext = {
appName: " Visual Studio Code ", // Will be trimmed and normalized
windowTitle: "main.ts - my-project",
focusedRole: "editor",
focusedName: "Text Editor",
// Simulated UI tree text (in real usage, capture from OS accessibility API)
uiTreeText: `
Window: Visual Studio Code
Toolbar:
Button: New File
Button: Save
Editor:
TextArea: main.ts content
StatusBar:
Label: Ln 42, Col 15
`,
confidence: 0.95,
};

// Create provider with capture function
const desktopProvider = new OpenClawDesktopAccessibilityEvidenceProvider(
() => desktopContext,
);

// Build evidence with canonical hashing
const desktopEvidence = await buildDesktopEvidenceFromProvider(
desktopProvider,
{ useCanonicalHash: true },
);

console.log("\nDesktop Evidence:");
console.log(JSON.stringify(desktopEvidence, null, 2));

// Demonstrate hash stability with whitespace variations
const desktopContext2: DesktopRuntimeContext = {
...desktopContext,
appName: "Visual Studio Code", // Same after normalization
// Same content with different whitespace
uiTreeText: `Window: Visual Studio Code
Toolbar:
Button: New File
Button: Save
Editor:
TextArea: main.ts content
StatusBar:
Label: Ln 42, Col 15`,
};

const desktopProvider2 = new OpenClawDesktopAccessibilityEvidenceProvider(
() => desktopContext2,
);

const desktopEvidence2 = await buildDesktopEvidenceFromProvider(
desktopProvider2,
{ useCanonicalHash: true },
);

console.log("\nDesktop Evidence (normalized variant):");
console.log(JSON.stringify(desktopEvidence2, null, 2));

const hashesMatch = desktopEvidence.state_hash === desktopEvidence2.state_hash;
console.log(`\nHashes match: ${hashesMatch ? "YES (canonicalization working)" : "NO"}`);
}

// ============================================================================
// Main
// ============================================================================

async function main(): Promise<void> {
console.log("OpenClaw Non-Web Evidence Provider Demo");
console.log("Demonstrates canonical hashing for terminal and desktop contexts\n");

await demoTerminalEvidence();
await demoDesktopEvidence();

console.log("\n" + "=".repeat(60));
console.log("Demo complete!");
console.log("=".repeat(60));
}

main().catch(console.error);
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "openclaw-predicate-provider",
"name": "predicate-claw",
"version": "0.1.0",
"description": "TypeScript OpenClaw security provider with Predicate Authority pre-execution checks.",
"main": "dist/src/index.js",
Expand All @@ -16,7 +16,7 @@
"license": "(MIT OR Apache-2.0)",
"type": "module",
"dependencies": {
"@predicatesystems/authority": "^0.3.2"
"@predicatesystems/authority": "^0.3.3"
},
"devDependencies": {
"@types/node": "^25.3.0",
Expand Down
Loading