@letterblack/lbe-core 1.3.4 → 1.3.6

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.
Files changed (78) hide show
  1. package/.githooks/pre-commit +2 -0
  2. package/.githooks/pre-push +2 -0
  3. package/CHANGELOG.md +81 -0
  4. package/LICENSE +1 -1
  5. package/README.md +158 -170
  6. package/RELEASE_WORKSPACE_RULES.md +179 -0
  7. package/Release-README.md +67 -0
  8. package/WORKSPACE.md +422 -0
  9. package/_proof.mjs +246 -0
  10. package/assets/runtime-boundary.svg +36 -36
  11. package/bin/lbe.js +12 -0
  12. package/config/identity.config.json +3 -0
  13. package/config/policy.default.json +24 -0
  14. package/dist/cli/lbe.js +4431 -0
  15. package/dist/hooks/register.cjs +505 -0
  16. package/dist/state/appendCentral.cjs +87 -0
  17. package/dist/state/index.cjs +101 -0
  18. package/exec/cli.js +472 -0
  19. package/exec/index.js +2 -0
  20. package/index.js +24 -0
  21. package/npm-pack.json +0 -0
  22. package/package.json +77 -45
  23. package/release/README.md +216 -0
  24. package/release/TRUST.md +90 -0
  25. package/release/exec-README.md +215 -0
  26. package/release/exec-types.d.ts +50 -0
  27. package/release-exec/LICENSE +1 -0
  28. package/release-exec/README.md +215 -0
  29. package/release-exec/assets/lbe-gates.jpg +0 -0
  30. package/release-exec/assets/lbe-gates.png +0 -0
  31. package/release-exec/assets/runtime-boundary.svg +36 -0
  32. package/release-exec/assets/story-allow.jpg +0 -0
  33. package/release-exec/assets/story-allow.png +0 -0
  34. package/release-exec/assets/story-deny.jpg +0 -0
  35. package/release-exec/assets/story-deny.png +0 -0
  36. package/release-exec/dist/cli.js +2841 -0
  37. package/release-exec/dist/index.js +1835 -0
  38. package/release-exec/dist/lbe_engine.wasm +0 -0
  39. package/{dist → release-exec/dist}/wasm.lock.json +4 -5
  40. package/release-exec/hooks/register.cjs +473 -0
  41. package/release-exec/package.json +35 -0
  42. package/release-exec/types.d.ts +50 -0
  43. package/runtime/engine.js +322 -0
  44. package/runtime/lbe_engine.wasm +0 -0
  45. package/src/cli/commands/assertConsumer.js +198 -0
  46. package/src/cli/commands/auditVerify.js +36 -0
  47. package/src/cli/commands/dryrun.js +175 -0
  48. package/src/cli/commands/health.js +153 -0
  49. package/src/cli/commands/init.js +306 -0
  50. package/src/cli/commands/integrityCheck.js +57 -0
  51. package/src/cli/commands/logs.js +53 -0
  52. package/src/cli/commands/openState.js +44 -0
  53. package/src/cli/commands/policyAdd.js +8 -0
  54. package/src/cli/commands/policyMode.js +7 -0
  55. package/src/cli/commands/policySign.js +72 -0
  56. package/src/cli/commands/proof.js +102 -0
  57. package/src/cli/commands/run.js +342 -0
  58. package/src/cli/commands/status.js +73 -0
  59. package/src/cli/commands/verify.js +144 -0
  60. package/src/cli/main.js +181 -0
  61. package/src/cli/parseArgs.js +115 -0
  62. package/src/exec/localExecutor.js +289 -0
  63. package/src/hooks/register.cjs +505 -0
  64. package/src/state/appendCentral.cjs +87 -0
  65. package/src/state/fileIndex.js +140 -0
  66. package/src/state/index.cjs +101 -0
  67. package/src/state/index.js +65 -0
  68. package/src/state/intentRegistry.js +84 -0
  69. package/src/state/migration.js +112 -0
  70. package/src/state/proofRunner.js +246 -0
  71. package/src/state/stateRoot.js +40 -0
  72. package/src/state/targetRegistry.js +109 -0
  73. package/src/state/workspaceId.js +40 -0
  74. package/src/state/workspaceRegistry.js +65 -0
  75. package/types.d.ts +175 -2
  76. package/dist/cli.js +0 -141
  77. package/dist/index.js +0 -52
  78. /package/dist/{lbe_engine.wasm → cli/lbe_engine.wasm} +0 -0
@@ -0,0 +1,50 @@
1
+ export interface LBEResult {
2
+ ok: boolean;
3
+ decision: 'allow' | 'deny' | 'observe';
4
+ executed: boolean;
5
+ dryRun: boolean;
6
+ error?: { code: string; message: string; recoverable: boolean };
7
+ matchedRules?: string[];
8
+ auditId?: string;
9
+ rollback?: { available: boolean; performed: boolean; backupId?: string };
10
+ }
11
+
12
+ export interface LBEPolicyRule {
13
+ effect: 'allow' | 'deny';
14
+ type: 'path' | 'command';
15
+ pattern: string;
16
+ from: string;
17
+ }
18
+
19
+ export interface LocalExecutor {
20
+ rootDir: string;
21
+
22
+ // High-level API — use these in agent code
23
+ writeFile(target: string, content: string): Promise<LBEResult>;
24
+ readFile(target: string): Promise<LBEResult>;
25
+ patchFile(target: string, content: string): Promise<LBEResult>;
26
+ deleteFile(target: string): Promise<LBEResult>;
27
+ runShell(cmd: string, args?: string[], opts?: { cwd?: string; timeoutMs?: number; maxOutputBytes?: number }): Promise<LBEResult>;
28
+
29
+ // Policy management
30
+ policy: {
31
+ read(): unknown;
32
+ proposeRule(rule: LBEPolicyRule): unknown;
33
+ addRule(rule: LBEPolicyRule): unknown;
34
+ };
35
+
36
+ // Audit
37
+ audit: { verify(): unknown };
38
+
39
+ // Low-level — for advanced / non-standard use only
40
+ validate(request: unknown): Promise<LBEResult>;
41
+ dryRun(request: unknown): Promise<LBEResult>;
42
+ execute(request: unknown): Promise<LBEResult>;
43
+ }
44
+
45
+ export function createLocalExecutor(options?: {
46
+ rootDir?: string;
47
+ keyId?: string;
48
+ mode?: 'observe' | 'enforce';
49
+ shell?: { allowCommands?: string[]; denyCommands?: string[]; maxRequests?: number };
50
+ }): LocalExecutor;
@@ -0,0 +1 @@
1
+ SEE LICENSE IN LICENSE
@@ -0,0 +1,215 @@
1
+ # @letterblack/lbe-exec
2
+
3
+ LBE puts a local policy gate between what an AI agent proposes and what the system actually executes. Every action — file write, shell command, anything — is validated locally before it runs. No cloud service. No daemon.
4
+
5
+ `lbe-exec` is the full in-process controller. It handles signing, execution, and auditing for you — your agent code just calls `lbe.writeFile()` or `lbe.runShell()`.
6
+
7
+ > **Used in production:** LBE is the safety engine inside [Letterblack for After Effects](https://letterblack.net) — every AI-generated script and automation command passes through it before touching a live project.
8
+
9
+ ---
10
+
11
+ ## Which package do you need?
12
+
13
+ | I want… | Package |
14
+ |---|---|
15
+ | LBE to handle file writes and shell commands for me (full controller) | `@letterblack/lbe-exec` ← you are here |
16
+ | Just the allow/deny decision — I'll execute it myself | `@letterblack/lbe-sdk` |
17
+
18
+ ---
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ npm install @letterblack/lbe-exec
24
+ npx lbe-exec init
25
+ ```
26
+
27
+ `npx lbe-exec init` creates `lbe.policy.json` in observer mode, generates `CLAUDE.md` and `.github/copilot-instructions.md` so AI agents automatically discover and follow governance, and writes `.lbe/AGENT_CONTRACT.md` as a machine-readable contract.
28
+
29
+ Requires Node.js ≥ 20.9.0.
30
+
31
+ ---
32
+
33
+ ## Quick start
34
+
35
+ ```js
36
+ import { createLocalExecutor } from '@letterblack/lbe-exec';
37
+
38
+ const lbe = createLocalExecutor({ rootDir: process.cwd() });
39
+
40
+ // Every call routes through the full 7-gate pipeline automatically
41
+ await lbe.writeFile('output/report.md', content);
42
+ await lbe.readFile('src/config.json');
43
+ await lbe.patchFile('src/index.js', patch);
44
+ await lbe.deleteFile('tmp/scratch.txt');
45
+ await lbe.runShell('node', ['scripts/build.js']);
46
+
47
+ // Result shape — same for every method
48
+ // { ok: true, decision: 'allow', executed: true, auditId: '...' }
49
+ // { ok: false, decision: 'deny', executed: false, error: { code, message } }
50
+ ```
51
+
52
+ No knowledge of the pipeline, request format, or policy internals required. All signing, validation, and auditing happens automatically.
53
+
54
+ ---
55
+
56
+ ## Options
57
+
58
+ ```js
59
+ const lbe = createLocalExecutor({
60
+ rootDir: process.cwd(), // sandbox root — no writes escape this path
61
+ mode: 'observe', // 'observe' (log only) or 'enforce' (block)
62
+ shell: {
63
+ allowCommands: ['node', 'npm'], // only these commands may run
64
+ denyCommands: ['rm', 'curl'], // always blocked regardless of policy
65
+ maxRequests: 20 // per-minute shell rate limit
66
+ }
67
+ });
68
+ ```
69
+
70
+ ---
71
+
72
+ ## Policy management
73
+
74
+ Only the host application writes policy. Agents may propose a rule — the proposal is returned as a plain object for the host to review. Until the host explicitly accepts and writes it, the proposal has no effect.
75
+
76
+ ```js
77
+ // Propose a rule — returns an object for the host to review, writes nothing
78
+ const proposal = lbe.policy.proposeRule({
79
+ effect: 'deny',
80
+ type: 'path',
81
+ pattern: 'secrets/**',
82
+ from: 'agent: these files should not be modified'
83
+ });
84
+
85
+ // Host accepts and writes the rule
86
+ lbe.policy.addRule(proposal);
87
+
88
+ // Read current policy
89
+ const policy = lbe.policy.read();
90
+
91
+ // Verify the audit chain has not been tampered with
92
+ lbe.audit.verify();
93
+ ```
94
+
95
+ ---
96
+
97
+ ## Observer mode — start here
98
+
99
+ Not ready to block? Start in observer mode. Every request is fully validated and logged exactly as it would be in enforcement — but nothing is blocked. Watch what the agent is doing before you decide what to deny.
100
+
101
+ ```bash
102
+ npx lbe-exec init # create lbe.policy.json in observer mode
103
+ npx lbe-exec enforce # switch to blocking
104
+ npx lbe-exec observe # switch back to advisory
105
+ ```
106
+
107
+ ---
108
+
109
+ ## CLI reference
110
+
111
+ | Command | Purpose |
112
+ |---|---|
113
+ | `npx lbe-exec init` | Bootstrap governance — policy, keys, agent files |
114
+ | `npx lbe-exec status` | Show mode, rule count, audit entry count |
115
+ | `npx lbe-exec policy` | List active rules |
116
+ | `npx lbe-exec observe` | Set advisory (log-only) mode |
117
+ | `npx lbe-exec enforce` | Set blocking mode |
118
+ | `npx lbe-exec execute` | Pipe a JSON request from stdin or `--input <file>` |
119
+
120
+ ---
121
+
122
+ ## How the gate pipeline works
123
+
124
+ ![LBE gate sequence — Request flows through Policy, Identity, and Scope gates before reaching Action. A rejected request is routed to denial before it reaches execution.](https://unpkg.com/@letterblack/lbe-exec/assets/lbe-gates.jpg)
125
+
126
+ Every request enters a 7-gate pipeline. A failure at any gate returns a structured denial — the remaining gates are not evaluated.
127
+
128
+ ```
129
+ [1] Schema required fields and structural validity
130
+
131
+ [2] Timestamp permitted clock-skew window (±10 minutes)
132
+
133
+ [3] Key lifecycle trusted key, active, not expired
134
+
135
+ [4] Signature Ed25519 request authenticity (signed locally, no network)
136
+
137
+ [5] Rate limit per-requester sliding-window limit
138
+
139
+ [6] Nonce single-use replay protection
140
+
141
+ [7] Policy configured authorization (deny-wins)
142
+
143
+ allow / deny / error — structured result returned to host
144
+ ```
145
+
146
+ The executor signs every request with a host-held key before validation. No key material leaves the process.
147
+
148
+ ---
149
+
150
+ ## When a request is approved
151
+
152
+ ![Happy path — agent proposes action, identity confirmed, policy approved, governed write executed, audit chain extended, result returned to app.](https://unpkg.com/@letterblack/lbe-exec/assets/story-allow.jpg)
153
+
154
+ 1. The agent calls a convenience method — `lbe.writeFile()`, `lbe.runShell()`, etc.
155
+ 2. The executor constructs and signs the request locally with a host-held Ed25519 key.
156
+ 3. All seven gates pass. The project policy approves the action.
157
+ 4. The write or command executes inside the configured project root.
158
+ 5. The audit chain is extended — every approved action appends a hash-linked entry to `.lbe/audit.jsonl`, permanently verifiable, impossible to silently remove.
159
+ 6. A structured result returns: whether it succeeded, which rules matched, and the audit entry identifier.
160
+
161
+ ---
162
+
163
+ ## When a request is blocked
164
+
165
+ ![Deny path — rogue agent bypass attempt, policy gate immediate rejection, shell untouched, filesystem unchanged, immutable audit entry written, final state clean.](https://unpkg.com/@letterblack/lbe-exec/assets/story-deny.jpg)
166
+
167
+ 1. The agent attempts an action — whether by mistake, misconfiguration, or a deliberate bypass attempt.
168
+ 2. The policy gate closes immediately. The request is denied before any adapter is reached.
169
+ 3. The shell is untouched. The filesystem is unchanged.
170
+ 4. The denial is written to the immutable audit log — chain sealed, evidence preserved.
171
+
172
+ No partial execution. No silent failures. Denial is a first-class outcome, not an error.
173
+
174
+ ---
175
+
176
+ ## What this covers
177
+
178
+ | Threat | Gate |
179
+ |---|---|
180
+ | Agent writes outside the project root | Scope — sandbox path check |
181
+ | Replayed or stale request | Identity — nonce and timestamp |
182
+ | Tampered or expired key | Identity — key lifecycle |
183
+ | Excessive requests | Identity — rate limit |
184
+ | Action not permitted by project policy | Policy — deny-wins evaluation |
185
+ | Unauthorized shell command | Scope — explicit command allowlist |
186
+ | Injected payload (eval, exec, __proto__) | Content scan before pipeline |
187
+
188
+ ---
189
+
190
+ ## What ships
191
+
192
+ ```
193
+ dist/index.js In-process executor — createLocalExecutor()
194
+ dist/cli.js Local CLI (npx lbe-exec)
195
+ dist/lbe_engine.wasm Verified WASM runtime binary
196
+ dist/wasm.lock.json Runtime integrity lock (SHA-256 of wasm binary)
197
+ assets/lbe-gates.jpg Gate sequence diagram
198
+ assets/story-allow.jpg Approved-request storyboard
199
+ assets/story-deny.jpg Blocked-request storyboard
200
+ assets/runtime-boundary.svg Runtime boundary diagram
201
+ assets/lbe-gates.png Gate sequence diagram (full resolution)
202
+ assets/story-allow.png Approved-request storyboard (full resolution)
203
+ assets/story-deny.png Blocked-request storyboard (full resolution)
204
+ types.d.ts TypeScript declarations
205
+ ```
206
+
207
+ Source code, tests, keys, and runtime state are not included.
208
+
209
+ ---
210
+
211
+ ## Limits
212
+
213
+ This package governs actions routed through its executor. It does not provide kernel-level process isolation, network-egress control, multi-tenant separation, or a hosted control plane.
214
+
215
+ For the raw WASM runtime without a controller, see `@letterblack/lbe-sdk`.
@@ -0,0 +1,36 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="960" height="440" viewBox="0 0 960 440" role="img" aria-labelledby="title desc">
2
+ <title id="title">LetterBlack LBE runtime boundary</title>
3
+ <desc id="desc">An application sends a serialized request to the local LBE WASM validation runtime, which returns a structured allow, deny, or error decision to the application.</desc>
4
+ <defs>
5
+ <linearGradient id="panel" x1="0" x2="1"><stop stop-color="#172033"/><stop offset="1" stop-color="#202d47"/></linearGradient>
6
+ <linearGradient id="runtime" x1="0" x2="1"><stop stop-color="#3756d6"/><stop offset="1" stop-color="#7356d6"/></linearGradient>
7
+ <marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" fill="#8fa3c9"/></marker>
8
+ </defs>
9
+ <rect width="960" height="440" rx="24" fill="#0e1524"/>
10
+ <text x="56" y="65" fill="#f5f8ff" font-family="Arial, sans-serif" font-size="28" font-weight="700">Local validation boundary</text>
11
+ <text x="56" y="98" fill="#aab9d4" font-family="Arial, sans-serif" font-size="17">The host owns execution. LBE returns a deterministic decision before the host accepts an action.</text>
12
+
13
+ <rect x="55" y="165" width="220" height="170" rx="16" fill="url(#panel)" stroke="#405273"/>
14
+ <text x="165" y="220" text-anchor="middle" fill="#fff" font-family="Arial, sans-serif" font-size="21" font-weight="700">Host application</text>
15
+ <text x="165" y="254" text-anchor="middle" fill="#b9c7df" font-family="Arial, sans-serif" font-size="15">agent, tool, or service</text>
16
+ <text x="165" y="286" text-anchor="middle" fill="#7fe1c3" font-family="Arial, sans-serif" font-size="15">serializes request</text>
17
+
18
+ <line x1="285" y1="250" x2="402" y2="250" stroke="#8fa3c9" stroke-width="4" marker-end="url(#arrow)"/>
19
+ <text x="343" y="229" text-anchor="middle" fill="#aab9d4" font-family="Arial, sans-serif" font-size="14">JSON request</text>
20
+
21
+ <rect x="415" y="132" width="290" height="236" rx="18" fill="url(#runtime)" stroke="#9a8cff" stroke-width="2"/>
22
+ <text x="560" y="183" text-anchor="middle" fill="#fff" font-family="Arial, sans-serif" font-size="23" font-weight="700">LBE WASM runtime</text>
23
+ <line x1="453" y1="205" x2="667" y2="205" stroke="#bfc8ff" opacity=".55"/>
24
+ <text x="560" y="242" text-anchor="middle" fill="#f0efff" font-family="Arial, sans-serif" font-size="16">schema · timestamp · key lifecycle</text>
25
+ <text x="560" y="271" text-anchor="middle" fill="#f0efff" font-family="Arial, sans-serif" font-size="16">signature · rate limit · nonce · policy</text>
26
+ <text x="560" y="319" text-anchor="middle" fill="#d7d1ff" font-family="Arial, sans-serif" font-size="14">local, deterministic validation</text>
27
+
28
+ <line x1="718" y1="250" x2="825" y2="250" stroke="#8fa3c9" stroke-width="4" marker-end="url(#arrow)"/>
29
+ <text x="770" y="229" text-anchor="middle" fill="#aab9d4" font-family="Arial, sans-serif" font-size="14">structured result</text>
30
+
31
+ <rect x="838" y="165" width="80" height="170" rx="16" fill="url(#panel)" stroke="#405273"/>
32
+ <text x="878" y="215" text-anchor="middle" fill="#fff" font-family="Arial, sans-serif" font-size="16" font-weight="700">Host</text>
33
+ <text x="878" y="249" text-anchor="middle" fill="#7fe1c3" font-family="Arial, sans-serif" font-size="14">allow</text>
34
+ <text x="878" y="275" text-anchor="middle" fill="#ffadad" font-family="Arial, sans-serif" font-size="14">deny</text>
35
+ <text x="878" y="301" text-anchor="middle" fill="#ffd28a" font-family="Arial, sans-serif" font-size="14">error</text>
36
+ </svg>