@tuent/sentinel 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,19 +1,24 @@
1
1
  # @tuent/sentinel
2
2
 
3
- Runtime security for Claude Code. Sentinel evaluates a policy on every Claude Code tool call — before it runs — and records each decision to a hash-chained, signed audit trail.
3
+ **Runtime security for Claude Code.** Sentinel checks your policy on every Claude Code tool call — _before_ it runs — and writes every decision to a signed, tamper-evident audit trail. Install it, point it at your project, and Claude Code operates inside guardrails you control.
4
4
 
5
- It is deliberately narrow. This is not a general-purpose "agent security platform"; it is a Claude-Code-native enforcement layer that hooks Claude Code's own tool-call lifecycle.
5
+ Sentinel is purpose-built for Claude Code. Rather than loosely wrapping a general-purpose "agent platform," it hooks Claude Code's own tool-call lifecycle directly — enforcement happens at the exact point where the agent decides to act.
6
+
7
+ ## What you get
8
+
9
+ - **Enforcement before execution** — every tool call is evaluated against your policy and allowed or denied before it runs, not flagged after the fact.
10
+ - **A signed audit trail** — every decision appended to a hash-chained, Ed25519-signed trail anchored by a signed manifest, verifiable end to end.
11
+ - **Automatic escalation** — repeated violations move the agent normal → restricted → quarantined at thresholds you set, and one command restores it.
12
+ - **Behavioral baseline** — a per-workspace baseline surfaces deviation signals as advisory context.
6
13
 
7
14
  ## How it works
8
15
 
9
- `init` installs a hook into Claude Code's PreToolUse lifecycle. Each tool call is routed to a local gateway daemon, evaluated against your policy, and allowed or denied before execution. Every decision is appended to a signed, hash-chained audit trail.
16
+ `init` installs a hook into Claude Code's PreToolUse lifecycle. Each tool call is routed to a local gateway daemon, evaluated against your policy, and allowed or denied before execution then recorded to the signed trail.
10
17
 
11
18
  ```
12
19
  Claude Code tool call → PreToolUse hook → gateway daemon → policy decision → signed audit
13
20
  ```
14
21
 
15
- Enforcement is cooperative: it depends on Claude Code invoking the hook. Sentinel is not a sandbox and does not contain a hostile agent that bypasses the hook.
16
-
17
22
  ## Install
18
23
 
19
24
  ```sh
@@ -21,9 +26,7 @@ npm install @tuent/sentinel
21
26
  npx sentinel init claude-code
22
27
  ```
23
28
 
24
- `init` writes a `.sentinel.yaml` policy into your project, sets up the gateway hook, and merges a hook entry into `.claude/settings.local.json`. No `tsx` or build step is required — the gateway ships as a runnable daemon.
25
-
26
- Requires Node.js ≥ 20. ESM-only.
29
+ `init` writes a `.sentinel.yaml` policy into your project, sets up the gateway hook, and merges a hook entry into `.claude/settings.local.json`. No build step — the gateway ships as a runnable daemon. Requires Node.js ≥ 20 (ESM-only).
27
30
 
28
31
  ## Policy
29
32
 
@@ -64,32 +67,25 @@ As policy violations accumulate, the agent escalates through modes: normal → r
64
67
 
65
68
  ## Audit trail
66
69
 
67
- Every decision is appended to a hash-chained trail and signed with Ed25519, anchored by a signed manifest. A verify check validates the hash chain and the entry signatures; the signed manifest anchors the chain.
70
+ Every decision is appended to a hash-chained trail and signed with Ed25519, anchored by a signed manifest. Run `sentinel --verify-audit` to validate the chain and every entry signature.
68
71
 
69
- Scope and limits — please read:
72
+ ## Behavioral analytics
70
73
 
71
- - The trail is designed for a single writer (the gateway daemon). There is no inter-process write lock. Running concurrent writer processes against one trail can fork the chain, so a verify-audit failure can indicate benign concurrency rather than tampering.
72
- - Signature determinism currently relies on V8's JSON key ordering. Verifying on another engine (Bun, Deno) is not yet supported and may report false invalids.
73
- - The trail is stored in plaintext. It is tamper-evident, not tamper-proof: a compromised same-host process can alter it, and the signed manifest is designed to detect that, not prevent it.
74
+ Sentinel maintains a per-workspace behavioral baseline and surfaces session-level deviation signals as advisory context. These are observational and do not block tool calls; the richer signals require a matured workspace baseline.
74
75
 
75
- ## Behavioral analytics
76
+ ## If a tool call is unexpectedly blocked
76
77
 
77
- Sentinel maintains a per-workspace behavioral baseline and surfaces session-level, advisory deviation signals. These are observational; they do not block tool calls. Activity-absence can surface once a baseline exists; temporal, access-pattern, and the remaining deviation signals require a matured workspace baseline. None fire on a fresh install.
78
+ Sentinel matches forbidden targets conservatively, which can occasionally deny a benign command that only _references_ a sensitive filename for example, searching your code with `grep` for `.env`. Plain mentions under safe commands (`echo`, comments) pass through. If a false positive restricts or quarantines the agent, restore it in one step:
78
79
 
79
- ## What Sentinel does not do
80
+ ```sh
81
+ sentinel release
82
+ ```
80
83
 
81
- - It does not defend against prompt injection of the agent itself.
82
- - In log-adapter mode it does not block in real time (it observes, with latency).
83
- - It does not prevent log tampering by a compromised same-host process.
84
- - It does not detect encrypted or obfuscated exfiltration.
85
- - It does not defend against multi-agent coordinated activity.
86
- - It does not resolve symlinks in target paths.
87
- - Workspace identity is a non-cryptographic 32-bit hash. It distinguishes local workspaces; it is not collision-resistant.
84
+ This records the change in the audit trail. Don't edit the mode state file by hand — that desyncs the trail from the live state.
88
85
 
89
- ## Notes
86
+ ## Security model
90
87
 
91
- - Target and content matching is conservative and can produce false positivesfor example, a literal token such as `process.env` can match a `.env` forbid pattern. Tune your policy accordingly.
92
- - On a cold start, the first tool call of a fresh session waits up to ~5 seconds for the daemon to warm up. If it is not ready, Sentinel applies its tiered fallback: high-sensitivity tools are denied, lower-sensitivity tools are allowed through.
88
+ Sentinel's enforcement is **cooperative** — it works by intercepting Claude Code's tool-call hook and the audit trail is **tamper-evident**, not tamper-proof. For the full threat model what Sentinel defends against, what's out of scope by design, and current v0.1.0 limitations see [SECURITY_MODEL.md](./SECURITY_MODEL.md).
93
89
 
94
90
  ## License
95
91
 
@@ -0,0 +1,231 @@
1
+ # Sentinel Security Model
2
+
3
+ ## What Sentinel Protects Against
4
+
5
+ ### Pre-Execution Enforcement
6
+
7
+ When using `wrap()` or `wrapTool()`, Sentinel validates every action before the agent executes it. HIGH and CRITICAL severity actions are blocked -- the agent's execution function never runs. The dangerous file is never read, the unauthorized API is never called, the forbidden command is never executed.
8
+
9
+ This is the strongest integration mode. The agent code passes its intended action and an execute function to Sentinel. If the action violates the role definition or targets a high-sensitivity resource, Sentinel returns `{ blocked: true }` and the execute function is never invoked. LOW and MEDIUM findings are informational -- the action still executes, and the finding is returned alongside the result for logging.
10
+
11
+ ### Role Violations
12
+
13
+ Agents are bound to a defined set of allowed actions. If an agent with `allowedActions: ["file_read", "file_write"]` attempts a `command_exec` or `database_query`, Sentinel produces a **HIGH** `role_violation` finding immediately.
14
+
15
+ ### Unauthorized Target Access
16
+
17
+ Targets are checked against glob patterns in two layers:
18
+
19
+ 1. **Forbidden patterns** (checked first) -- any match produces a **HIGH** `unauthorized_target`
20
+ 2. **Allowed patterns** (checked second) -- access outside allowed scope produces a **MEDIUM** `scope_violation`
21
+
22
+ Pattern matching uses `**` (any path) and `*` (single segment). Examples:
23
+
24
+ - `**/.env` matches `.env` at any depth
25
+ - `**/.ssh/**` matches anything inside any `.ssh` directory
26
+ - `src/**` matches all files under `src/`
27
+
28
+ ### Behavioral Anomalies
29
+
30
+ After a baseline is computed from historical sessions, the DeviationDetector checks every new session for:
31
+
32
+ - **Volume spikes** -- event count exceeds 3x the baseline average
33
+ - **Unusual targets** -- more than 50% of accessed files are outside the agent's typical target set
34
+ - **Weight anomaly** -- session engagement exceeds baseline normal range + 0.3 threshold
35
+ - **Category shifts** -- activity type represents less than 5% of baseline behavior
36
+ - **Activity drops** -- session event count drops 75%+ below baseline average (possible log suppression)
37
+ - **Activity gaps** -- agent silence exceeds 2-3x the longest observed gap in the baseline period
38
+ - **Convergence escalation** -- when 3+ MEDIUM findings occur in one session, the first is escalated to HIGH
39
+
40
+ ### High-Sensitivity Target Access
41
+
42
+ The TargetSensitivityScorer assigns risk scores (0.0-1.0) to every target based on built-in pattern rules. Scores are multiplied by action type (writes are 1.3x, command execution is 1.5x). When the effective score reaches 0.9+, findings are escalated to **CRITICAL**.
43
+
44
+ Built-in sensitivity categories:
45
+
46
+ - **Credentials** (1.0): `.ssh/`, `.env`, `.aws/`, `secrets/`, `id_rsa*`, `.pem`, `shadow`
47
+ - **System** (0.7-0.9): `/etc/`, `passwd`
48
+ - **PII** (0.8-0.85): `users/`, `customers/`, `payments/`
49
+ - **Database** (0.75-0.8): `database/`, `.sqlite`, `.db`
50
+ - **Config** (0.5-0.75): `config/`, `config/production*`
51
+ - **Source** (0.1-0.45): `src/`, `tests/`, `docs/`, `.git/`
52
+ - **Default** (0.15): anything not matching a specific rule
53
+
54
+ Custom rules can be injected via the `TargetSensitivityScorer` constructor, overriding built-in patterns.
55
+
56
+ ### Temporal Anomalies
57
+
58
+ Agents can have an `expectedSchedule` with allowed days and hours (UTC). Activity outside the schedule produces a **MEDIUM** `temporal_anomaly`. The DeviationDetector independently checks against the baseline's typical active hours and days.
59
+
60
+ ### Path Traversal Attempts
61
+
62
+ All target paths are normalized with `path.normalize()` before pattern matching. This resolves `../` and `./` sequences, preventing bypass attempts like:
63
+
64
+ - `src/../.env` is normalized to `.env` before checking against `**/.env`
65
+ - `project/subdir/../../.ssh/id_rsa` is normalized to `.ssh/id_rsa`
66
+
67
+ ---
68
+
69
+ ## What Sentinel Does NOT Protect Against
70
+
71
+ ### Prompt Injection
72
+
73
+ Sentinel monitors agent **actions**, not agent **prompts**. It cannot detect or prevent prompt injection attacks directly. However, if a prompt injection causes an agent to perform a forbidden action (e.g., reading `.env`), that action **will** be caught by role validation.
74
+
75
+ ### Real-Time Blocking in Log Adapter Mode
76
+
77
+ When using the LogAdapter (file-tailing mode), events are detected after they occur with a polling delay (default ~2 seconds). This mode is observation-only -- it cannot block actions.
78
+
79
+ For real-time pre-execution enforcement, use one of:
80
+
81
+ - **`sentinel.wrap()` / `sentinel.wrapTool()`** (recommended) -- synchronous enforcement, actions blocked before execution
82
+ - **`sentinel.check()`** -- manual pre-execution gate in your agent orchestrator
83
+ - **WebhookReceiver** in synchronous mode (agent sends event before executing)
84
+
85
+ ### Log Tampering by Compromised Agents
86
+
87
+ If an AI agent and Sentinel run on the same host and the agent has write access to the filesystem, a compromised agent could theoretically modify its own log files or audit trail. **Mitigation:** Run Sentinel on a separate host and ingest events via the webhook adapter.
88
+
89
+ ### Encrypted or Obfuscated Data Exfiltration
90
+
91
+ Sentinel sees which targets an agent accesses and what actions it performs. It does not inspect the content of data being read or transmitted. An agent reading a sensitive file and encoding its contents into an innocuous-looking API call would be partially detected (the file read is logged) but the exfiltration channel may not be flagged.
92
+
93
+ ### Multi-Agent Coordinated Attacks
94
+
95
+ Each agent is monitored independently by default. The `CorrelationDetector` provides basic cross-agent pattern detection (e.g., read-then-exfiltrate chains), but sophisticated coordinated attacks where each agent stays within its own role may evade detection.
96
+
97
+ ### Symlink Resolution
98
+
99
+ RoleValidator operates on path strings, not the filesystem. A symlink named `src/safe-link` pointing to `/etc/shadow` would pass the `src/**` allowed pattern check. Sentinel cannot resolve symlinks without filesystem access on the target host.
100
+
101
+ ### Concurrent Audit-Trail Writers (No Inter-Process Lock)
102
+
103
+ The audit trail is designed for a **single writer** -- the gateway daemon. There is no inter-process write lock. Running concurrent writer processes against one trail can fork the hash chain, so a `verify-audit` failure can indicate benign concurrency rather than tampering.
104
+
105
+ ### Cross-Engine Signature Verification
106
+
107
+ Ed25519 signature determinism currently relies on **V8's JSON key ordering** when serializing the signed payload. Verifying on another JavaScript engine (Bun, Deno) is not supported and may report false invalids.
108
+
109
+ ### Workspace Identity Collisions
110
+
111
+ Workspace identity is a **32-bit FNV-1a hash** of the workspace root. It is non-cryptographic and not collision-resistant; it distinguishes local workspaces but is not a security boundary.
112
+
113
+ ### Conservative Matching / False Positives
114
+
115
+ Target matching is deliberately conservative: a command that only _references_ a forbidden filename — for instance, a `grep` whose pattern or path is `.env` — can be denied even though it never opens the file. Because each denial counts toward the escalation ladder, a run of such false positives can move the agent to `restricted` or `quarantined`. Plain mentions under safe verbs (`echo`, `printf`) and in comments are not flagged. When a false positive does restrict or quarantine the agent, recover with `sentinel release`, which records the change in the audit trail. Tune policies to widen the allow set where this proves noisy.
116
+
117
+ ### Cold-Start Window
118
+
119
+ On a cold start, the first tool call of a fresh session waits up to **~5 seconds** for the gateway daemon to warm up. If the daemon is not ready, the hook applies its **tiered fallback** rather than failing closed uniformly: high-sensitivity tools (e.g. `Bash`, `Write`, `Edit`, `WebFetch`) are **denied**, while lower-sensitivity tools (e.g. `Read`, `Glob`, `Grep`, `WebSearch`) are **allowed** through. MCP and unknown tools default to the high (deny) tier.
120
+
121
+ ---
122
+
123
+ ## Trust Model
124
+
125
+ ### Recommended Production Deployment
126
+
127
+ ```
128
+ +------------------+ HTTPS/webhook +-------------------+
129
+ | AI Agent Host | -----------------------------> | Sentinel Host |
130
+ | | POST /api/sentinel/events | |
131
+ | Agent runtime | | WebhookReceiver |
132
+ | sends events | | RoleValidator |
133
+ | before/after | | AuditTrail |
134
+ | each action | | AlertManager |
135
+ +------------------+ +-------------------+
136
+ ```
137
+
138
+ - Sentinel runs on a separate host the agent cannot access
139
+ - Events are pushed via authenticated webhook (API key in Authorization header)
140
+ - Audit trail is on the Sentinel host, inaccessible to the agent
141
+ - Alerts dispatch to external systems (Slack webhook, PagerDuty, etc.)
142
+
143
+ ### Development Deployment
144
+
145
+ ```
146
+ +----------------------------------------------+
147
+ | Same Host |
148
+ | |
149
+ | Agent runtime ---> activity.log |
150
+ | | |
151
+ | LogAdapter (polls) |
152
+ | | |
153
+ | SentinelRunner |
154
+ | AuditTrail |
155
+ +----------------------------------------------+
156
+ ```
157
+
158
+ - Sentinel watches the agent's log file via LogAdapter
159
+ - Simpler setup but agent could tamper with its own logs
160
+ - Suitable for development and testing
161
+
162
+ ### Audit Trail Integrity
163
+
164
+ - Append-only: entries are only written, never modified or deleted programmatically
165
+ - Per-agent: each agent has its own `audit.log` file
166
+ - Rotation: files rotate at 10 MB, keeping 3 archives (audit.log.1, .2, .3)
167
+ - All entries include timestamps and are queryable by time range and severity
168
+
169
+ ### Data Storage
170
+
171
+ - All data is local to the Sentinel host filesystem
172
+ - No cloud services, no external databases, no network dependencies
173
+ - Storage location is configurable via `agentsDir` constructor parameter
174
+ - Default: `~/.dahlia/agents/`
175
+
176
+ ---
177
+
178
+ ## Severity Levels
179
+
180
+ | Severity | Meaning | Triggers Alert? | Response |
181
+ | ------------ | ------------------------------ | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
182
+ | **LOW** | Notable but expected variation | No (unless configured) | Log for review. Weight anomalies, off-day activity. |
183
+ | **MEDIUM** | Warrants monitoring | Configurable | Single deviation signal. Volume spike, unusual targets, category shift, off-hours. Escalates to HIGH when 3+ converge in one session. |
184
+ | **HIGH** | Requires investigation | Yes | Role violation, forbidden target access, escalated convergence. |
185
+ | **CRITICAL** | Immediate response required | Yes | Target sensitivity effective score >= 0.9. Credential access, system file writes. |
186
+
187
+ ### Escalation Rules
188
+
189
+ - MEDIUM findings escalate to HIGH when 3+ MEDIUM findings occur in a single session (convergence escalation)
190
+ - HIGH findings are escalated to CRITICAL by the TargetSensitivityScorer when the effective score (sensitivity x action multiplier) reaches 0.9+
191
+
192
+ ---
193
+
194
+ ## Detection Capabilities
195
+
196
+ | Threat | Detection Method | Component | Expected Severity |
197
+ | --------------------------------------------- | -------------------------------------------- | -------------------------- | --------------------------------------------- |
198
+ | SSH key access (`~/.ssh/id_rsa`) | Target sensitivity (1.0) | RoleValidator + Scorer | CRITICAL |
199
+ | `.env` file read | Target sensitivity (1.0) | RoleValidator + Scorer | CRITICAL |
200
+ | AWS credential access (`~/.aws/credentials`) | Target sensitivity (1.0) | RoleValidator + Scorer | CRITICAL |
201
+ | Kubernetes config access (`~/.kube/config`) | Target sensitivity (0.95) | RoleValidator + Scorer | CRITICAL |
202
+ | Write to `~/.ssh/authorized_keys` (backdoor) | Target sensitivity (1.0 x 1.3 write) | RoleValidator + Scorer | CRITICAL |
203
+ | `/etc/passwd` read | Target sensitivity (0.9) + forbidden pattern | RoleValidator + Scorer | CRITICAL |
204
+ | System config write (`/etc/nginx/nginx.conf`) | Target sensitivity (0.7 x 1.3 write = 0.91) | RoleValidator + Scorer | CRITICAL |
205
+ | Payment data access (`payments/`) | Target sensitivity (0.85) | DeviationDetector + Scorer | HIGH |
206
+ | Database query by file-only agent | Action not in allowedActions | RoleValidator | HIGH |
207
+ | Shell command by code-writer agent | Action not in allowedActions | RoleValidator | HIGH |
208
+ | 3x baseline event volume | Event count exceeds threshold | DeviationDetector | MEDIUM (escalates to HIGH with other signals) |
209
+ | >50% novel target files in session | Target set comparison | DeviationDetector | MEDIUM |
210
+ | Activity outside expected hours | Schedule check (UTC) | RoleValidator | MEDIUM |
211
+ | Weekend activity when weekday-only | Schedule check (UTC) | RoleValidator | MEDIUM |
212
+ | Path traversal (`src/../.env`) | Path normalization + pattern match | RoleValidator | HIGH/CRITICAL (depends on target) |
213
+ | Category shift (rare action type) | Baseline action distribution | DeviationDetector | MEDIUM |
214
+ | Access outside allowed scope | Allowed target pattern miss | RoleValidator | MEDIUM |
215
+ | Multiple converging anomalies (3+) | Finding count in session | DeviationDetector | MEDIUM escalated to HIGH |
216
+ | Activity drop (75%+ fewer events) | Event count vs baseline average | DeviationDetector | MEDIUM (HIGH at 90%+ drop) |
217
+ | Extended agent silence (2-3x max gap) | Time since last event vs baseline gaps | DeviationDetector | LOW (MEDIUM at 3x+) |
218
+
219
+ ---
220
+
221
+ ## Finding Types
222
+
223
+ | Type | Source | Description |
224
+ | --------------------- | -------------------------------- | ----------------------------------------------------------------------------------------- |
225
+ | `role_violation` | RoleValidator | Agent performed an action not in its `allowedActions` list |
226
+ | `unauthorized_target` | RoleValidator, DeviationDetector | Agent accessed a target matching `forbiddenTargetPatterns` or scoring high on sensitivity |
227
+ | `scope_violation` | RoleValidator | Agent accessed a target outside its `allowedTargetPatterns` |
228
+ | `temporal_anomaly` | RoleValidator, DeviationDetector | Activity outside expected schedule or baseline typical hours/days |
229
+ | `volume_spike` | DeviationDetector | Session event count exceeds baseline average by 3x+ |
230
+ | `access_pattern` | DeviationDetector | Unusual target distribution, weight anomaly, or category shift |
231
+ | `behavioral_absence` | DeviationDetector | Significant activity drop (75%+ below baseline) or extended silence (2-3x max gap) |
@@ -0,0 +1,10 @@
1
+ import {
2
+ Sentinel
3
+ } from "./chunk-NS6ZLMDK.js";
4
+ import "./chunk-QHE56MEO.js";
5
+ import "./chunk-2FFMYSVC.js";
6
+ import "./chunk-NUXSUSYY.js";
7
+ export {
8
+ Sentinel
9
+ };
10
+ //# sourceMappingURL=Sentinel-QHMQ67W3.js.map
@@ -0,0 +1,32 @@
1
+ // src/workspaceIdentity.ts
2
+ var AGENT_PREFIX = "claude-code";
3
+ function fnv1a32Hex(s) {
4
+ let h = 2166136261;
5
+ for (let i = 0; i < s.length; i++) {
6
+ h ^= s.charCodeAt(i);
7
+ h = Math.imul(h, 16777619);
8
+ }
9
+ return (h >>> 0).toString(16).padStart(8, "0");
10
+ }
11
+ function lastSegment(path) {
12
+ const parts = path.split("/").filter(Boolean);
13
+ return parts.length > 0 ? parts[parts.length - 1] : "";
14
+ }
15
+ function slugify(s) {
16
+ return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
17
+ }
18
+ function normalizeRoot(root) {
19
+ if (root === "" || root === "/") return root;
20
+ return root.replace(/\/+$/, "") || "/";
21
+ }
22
+ function deriveAgentId(workspaceRoot) {
23
+ const root = normalizeRoot(workspaceRoot);
24
+ const slug = slugify(lastSegment(root)) || "root";
25
+ const hash = fnv1a32Hex(root);
26
+ return `${AGENT_PREFIX}@${slug}-${hash}`;
27
+ }
28
+
29
+ export {
30
+ deriveAgentId
31
+ };
32
+ //# sourceMappingURL=chunk-B5QKJHSV.js.map