Skip to content

Commit 6914332

Browse files
dormsternclaude
andcommitted
docs: extract API reference, policy examples, security to docs/API.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 6e24861 commit 6914332

1 file changed

Lines changed: 195 additions & 0 deletions

File tree

docs/API.md

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
# declawed API Reference
2+
3+
## `createShield(configOrPath, options?)`
4+
5+
Create a governance-wrapped AnchorBrowser session.
6+
7+
```typescript
8+
// From YAML file
9+
const shield = createShield('./shield.yaml')
10+
11+
// From inline config
12+
const shield = createShield({
13+
allow: ['read*'],
14+
deny: ['*send*'],
15+
default: 'deny',
16+
expire: '60min',
17+
maxActions: 100,
18+
agent: 'my-agent',
19+
})
20+
21+
// With options
22+
const shield = createShield('./shield.yaml', {
23+
anchorApiKey: 'your-key', // default: process.env.ANCHOR_API_KEY
24+
auditPath: './logs/audit.jsonl', // default: ./shield-audit.jsonl
25+
})
26+
```
27+
28+
## `shield.task(description)`
29+
30+
Execute a task through the governance layer.
31+
32+
```typescript
33+
const result = await shield.task('read my inbox')
34+
// → { allowed: true, output: '...', auditId: 'evt-...' }
35+
// → { allowed: false, reason: 'blocked by deny pattern: *send*', auditId: 'evt-...' }
36+
```
37+
38+
## `shield.kill()`
39+
40+
Destroy the AnchorBrowser session immediately.
41+
42+
```typescript
43+
await shield.kill()
44+
// Session destroyed, event logged, done.
45+
```
46+
47+
## `shield.audit()`
48+
49+
Return all audit events.
50+
51+
```typescript
52+
const events = shield.audit()
53+
// → [{ id, timestamp, agent, task, action, reason?, duration? }, ...]
54+
```
55+
56+
## `shield.status()`
57+
58+
Return current session stats.
59+
60+
```typescript
61+
const status = shield.status()
62+
// → { active: true, agent: 'inbox-assistant', uptime: '47min', allowed: 23, blocked: 3 }
63+
```
64+
65+
## Policy Examples
66+
67+
### Restrictive (read-only inbox)
68+
69+
```yaml
70+
agent: inbox-reader
71+
rules:
72+
allow:
73+
- "read*"
74+
- "list*"
75+
- "search*"
76+
deny:
77+
- "*"
78+
default: deny
79+
expire_after: 30min
80+
```
81+
82+
### Permissive (block dangerous actions only)
83+
84+
```yaml
85+
agent: sales-assistant
86+
rules:
87+
deny:
88+
- "*delete*"
89+
- "*password*"
90+
- "*settings*"
91+
- "*admin*"
92+
default: allow
93+
expire_after: 8h
94+
max_actions: 500
95+
```
96+
97+
### Time-boxed (one-off task)
98+
99+
```yaml
100+
agent: report-generator
101+
rules:
102+
allow:
103+
- "read*"
104+
- "export*"
105+
- "download*"
106+
deny:
107+
- "*send*"
108+
- "*delete*"
109+
default: deny
110+
expire_after: 15min
111+
max_actions: 20
112+
```
113+
114+
### Inline (no YAML file)
115+
116+
```typescript
117+
const shield = createShield({
118+
allow: ['read*', 'list*'],
119+
deny: ['*send*', '*delete*'],
120+
default: 'deny',
121+
expire: '60min',
122+
maxActions: 100,
123+
agent: 'my-agent',
124+
})
125+
```
126+
127+
## Pattern Matching
128+
129+
- `*` matches any characters (glob-style via [picomatch](https://github.com/micromatch/picomatch))
130+
- Matching is **case-insensitive** on the full task string
131+
- **Deny rules checked first** — deny always takes priority
132+
- If no match → falls back to `default` (`deny` or `allow`)
133+
- Invisible Unicode characters are stripped before matching (zero-width spaces, combining diacriticals, BiDi controls)
134+
135+
## Audit Log Format
136+
137+
Every action is logged to `shield-audit.jsonl`:
138+
139+
```jsonl
140+
{"id":"evt-1708300000-x4k2m","timestamp":"2026-02-19T10:00:00.000Z","agent":"inbox-assistant","task":"read my inbox","action":"allowed","duration":2340}
141+
{"id":"evt-1708300003-j9f1p","timestamp":"2026-02-19T10:00:03.000Z","agent":"inbox-assistant","task":"send message to Bob","action":"blocked","reason":"blocked by deny pattern: *send*"}
142+
{"id":"evt-1708300010-m3n7q","timestamp":"2026-02-19T10:00:10.000Z","agent":"inbox-assistant","task":"search emails from Q4","action":"allowed","duration":1890}
143+
```
144+
145+
JSONL format means:
146+
- **Append-only** — events can't be edited or deleted
147+
- **Portable** — pipe to jq, import into any SIEM
148+
- **Zero infra** — just a file on disk
149+
150+
## Kill Switch
151+
152+
### From code
153+
154+
```typescript
155+
await shield.kill()
156+
```
157+
158+
### From terminal
159+
160+
```bash
161+
npx declawed kill
162+
# → Session mock-session-123 killed.
163+
```
164+
165+
### Check status
166+
167+
```bash
168+
npx declawed status
169+
# Agent: inbox-assistant
170+
# Status: active
171+
# Allowed: 23
172+
# Blocked: 3
173+
# Total: 27
174+
175+
npx declawed audit
176+
# Time Action Task
177+
# ─────────────────────────────────────────────
178+
# 2026-02-19 10:00:00 allowed read my inbox
179+
# 2026-02-19 10:00:03 blocked send message to Bob (blocked by deny...)
180+
```
181+
182+
## Security
183+
184+
- Deny-first evaluation — deny rules always take priority over allow
185+
- Unicode bypass protection — invisible characters stripped before pattern matching
186+
- YAML type validation — non-string patterns rejected at load time
187+
- Fail-closed — errors during execution are logged and reported as blocked
188+
- Session file permissions — restricted to owner-only (0o600)
189+
- Action budgets — only allowed tasks consume quota (blocked tasks are free)
190+
191+
For vulnerability reports, see [SECURITY.md](../SECURITY.md).
192+
193+
## Testing
194+
195+
61 tests covering the governance boundary: policy evaluation, deny-first ordering, Unicode bypass vectors, YAML validation, audit logging, budget enforcement, concurrent access, timer expiration, kill idempotency, and fail-closed behavior. AnchorBrowser is mocked because declawed's job is policy enforcement, not browser automation — if the browser fails, declawed fails closed.

0 commit comments

Comments
 (0)