@letterblack/lbe-core 1.3.0
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/.githooks/pre-commit +2 -0
- package/.githooks/pre-push +2 -0
- package/CHANGELOG.md +57 -0
- package/LICENSE +1 -0
- package/README.md +506 -0
- package/Release-README.md +339 -0
- package/WORKSPACE.md +422 -0
- package/_proof.mjs +246 -0
- package/assets/lbe-gates.jpg +0 -0
- package/assets/lbe-gates.png +0 -0
- package/assets/runtime-boundary.svg +36 -0
- package/assets/story-allow.jpg +0 -0
- package/assets/story-allow.png +0 -0
- package/assets/story-deny.jpg +0 -0
- package/assets/story-deny.png +0 -0
- package/bin/lbe.js +12 -0
- package/config/identity.config.json +3 -0
- package/config/policy.default.json +24 -0
- package/dist/cli/lbe.js +4274 -0
- package/dist/hooks/register.cjs +505 -0
- package/dist/state/appendCentral.cjs +87 -0
- package/dist/state/index.cjs +101 -0
- package/exec/cli.js +472 -0
- package/exec/index.js +2 -0
- package/index.js +24 -0
- package/lbe.audit.jsonl +46 -0
- package/package.json +76 -0
- package/release/README.md +216 -0
- package/release/TRUST.md +90 -0
- package/release/exec-README.md +215 -0
- package/release/exec-types.d.ts +50 -0
- package/release-exec/LICENSE +1 -0
- package/release-exec/README.md +215 -0
- package/release-exec/assets/lbe-gates.jpg +0 -0
- package/release-exec/assets/lbe-gates.png +0 -0
- package/release-exec/assets/runtime-boundary.svg +36 -0
- package/release-exec/assets/story-allow.jpg +0 -0
- package/release-exec/assets/story-allow.png +0 -0
- package/release-exec/assets/story-deny.jpg +0 -0
- package/release-exec/assets/story-deny.png +0 -0
- package/release-exec/dist/cli.js +2841 -0
- package/release-exec/dist/index.js +1835 -0
- package/release-exec/dist/lbe_engine.wasm +0 -0
- package/release-exec/dist/wasm.lock.json +4 -0
- package/release-exec/hooks/register.cjs +473 -0
- package/release-exec/package.json +35 -0
- package/release-exec/types.d.ts +50 -0
- package/runtime/engine.js +322 -0
- package/runtime/lbe_engine.wasm +0 -0
- package/src/cli/commands/auditVerify.js +36 -0
- package/src/cli/commands/dryrun.js +175 -0
- package/src/cli/commands/health.js +153 -0
- package/src/cli/commands/init.js +306 -0
- package/src/cli/commands/integrityCheck.js +57 -0
- package/src/cli/commands/logs.js +53 -0
- package/src/cli/commands/openState.js +44 -0
- package/src/cli/commands/policyAdd.js +8 -0
- package/src/cli/commands/policyMode.js +7 -0
- package/src/cli/commands/policySign.js +72 -0
- package/src/cli/commands/proof.js +122 -0
- package/src/cli/commands/run.js +342 -0
- package/src/cli/commands/status.js +73 -0
- package/src/cli/commands/verify.js +144 -0
- package/src/cli/main.js +176 -0
- package/src/cli/parseArgs.js +114 -0
- package/src/exec/localExecutor.js +289 -0
- package/src/hooks/register.cjs +505 -0
- package/src/state/appendCentral.cjs +87 -0
- package/src/state/fileIndex.js +140 -0
- package/src/state/index.cjs +101 -0
- package/src/state/index.js +65 -0
- package/src/state/intentRegistry.js +83 -0
- package/src/state/migration.js +112 -0
- package/src/state/proofRunner.js +246 -0
- package/src/state/stateRoot.js +40 -0
- package/src/state/targetRegistry.js +108 -0
- package/src/state/workspaceId.js +40 -0
- package/src/state/workspaceRegistry.js +65 -0
- package/types.d.ts +175 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.3.0 — 2026-06-23
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Central local workspace state, proof summaries, and one-time import of legacy
|
|
7
|
+
`.lbe/events.jsonl` entries while preserving the original local log.
|
|
8
|
+
|
|
9
|
+
## 1.2.0 — 2026-06-20
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **Real JS governance engine** — `createLBE()` now uses the full 7-gate validation pipeline (schema → key lifecycle → timestamp skew → rate limit → nonce replay → policy) with backup, rollback, and audit. Previously backed by a thin WASM wrapper.
|
|
13
|
+
- **Observer mode** — `createLBE({ mode: 'observe' })` or `npx lbe observe`. All gates run silently, audit log is written, nothing is blocked. Default for new and half-built projects.
|
|
14
|
+
- **Policy file** (`lbe.policy.json`) — human-readable rule store per project. Records `effect`, `type`, `pattern`, the original user message (`from`), and timestamp (`at`). Deny always wins over allow.
|
|
15
|
+
- **New CLI commands:**
|
|
16
|
+
- `npx lbe observe` — switch to observer mode
|
|
17
|
+
- `npx lbe enforce` — switch to enforcement mode
|
|
18
|
+
- `npx lbe policy` — list all rules with source context
|
|
19
|
+
- **Universal CLI interface** — non-JS projects (Python, Rust, Go, any language) can pipe JSON to `npx lbe execute`. Exit 0 = allowed, exit 1 = denied, exit 2 = error.
|
|
20
|
+
- **Language-agnostic design** — WASM runtime path documented as the path to non-JS bindings. JS engine is the current production runtime.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
- `LBEResult` now includes `commandId`, `stage`, `risk`, `output` fields.
|
|
24
|
+
- `LBEOptions` removes `policy_mode` / `timeout_ms` — these are managed by the governance engine internally.
|
|
25
|
+
- `wrapTools().dispatch()` now returns `Promise<LBEResult>` (async) to match the real engine contract.
|
|
26
|
+
- Types updated throughout to reflect observer mode result shape (`LBEObservedResult`).
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 1.0.4 — 2026-06-19
|
|
31
|
+
|
|
32
|
+
### Removed
|
|
33
|
+
- MCP execution surface — `lbe-mcp` command, MCP adapter, and configuration examples removed.
|
|
34
|
+
An MCP server only offers LBE as one optional tool; agent hosts with native tools can act outside that boundary, so it cannot enforce the governance boundary. See `docs/decisions/ADR-001-remove-mcp-execution-surface.md`.
|
|
35
|
+
- HTTP server surface — `lbe-serve` command and HTTP adapter removed.
|
|
36
|
+
An HTTP endpoint replicates governance in a separate process and creates a second attack surface without guaranteeing the calling agent routes through it. See `docs/decisions/ADR-002-remove-http-server-surface.md`.
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
- Established SDK-only product boundary: LBE ships as one local SDK and CLI embedded in the caller's application. No daemon, host platform, Docker deployment, or companion system. See `docs/decisions/ADR-003-sdk-only-product-boundary.md`.
|
|
40
|
+
- Workspace pruned to SDK-only source; all optional execution surfaces removed from `src/`, `bin/`, and CI config.
|
|
41
|
+
- Public package identity (`LBE_PUBLIC_PACKAGE_NAME`, `LBE_PUBLIC_PACKAGE_VERSION`) is now parameterised via environment variables in the build script.
|
|
42
|
+
- Test command made portable across Node.js versions (no `--experimental-vm-modules` flag needed).
|
|
43
|
+
|
|
44
|
+
### Public surface
|
|
45
|
+
The public package (`@letterblack/lbe-sdk`) exports exactly one function:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
export function execute(input: string): string;
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
No server, daemon, MCP surface, or optional execution layer ships in the public package.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 1.0.3 and earlier
|
|
56
|
+
|
|
57
|
+
Pre-release development. No public changelog maintained.
|
package/LICENSE
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
SEE LICENSE IN LICENSE
|
package/README.md
ADDED
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
# LetterBlack LBE
|
|
2
|
+
|
|
3
|
+
LBE is local execution control for AI agents. It evaluates file and shell
|
|
4
|
+
actions routed through its execution boundary, records local evidence, and
|
|
5
|
+
returns an allow/deny outcome before the governed action runs.
|
|
6
|
+
|
|
7
|
+
LBE is not an AI model, IDE, full OS sandbox, or cloud monitor. It does not
|
|
8
|
+
control direct actions outside its execution boundary.
|
|
9
|
+
|
|
10
|
+
## Install and start
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @letterblack/lbe-sdk
|
|
14
|
+
npx lbe init
|
|
15
|
+
npx lbe status
|
|
16
|
+
npx lbe logs
|
|
17
|
+
npx lbe proof --public
|
|
18
|
+
npx lbe open-state
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`init` creates project policy material. `status` shows the local central state
|
|
22
|
+
for the current workspace; `logs` reads its event history; `proof` shows the
|
|
23
|
+
latest proof result; and `open-state` opens the central state folder.
|
|
24
|
+
|
|
25
|
+
## Local state and proof
|
|
26
|
+
|
|
27
|
+
LBE keeps state locally in a central per-user state folder. Each workspace has
|
|
28
|
+
a stable workspace ID and its own event log. In v1.3, an existing
|
|
29
|
+
`.lbe/events.jsonl` remains local fallback truth and is imported into central
|
|
30
|
+
state once; the source file is preserved.
|
|
31
|
+
|
|
32
|
+
Proof combines an intent, optional target, file index, LBE events, and
|
|
33
|
+
`proof/latest.json`. Use `lbe proof --public` for a redacted proof summary.
|
|
34
|
+
Non-inspectable targets can produce `WEAK_PROOF` rather than a stronger claim.
|
|
35
|
+
|
|
36
|
+
## Limits
|
|
37
|
+
|
|
38
|
+
Only actions routed through LBE are controlled. Central writes are best-effort,
|
|
39
|
+
logs remain local, and LBE does not provide process isolation or network
|
|
40
|
+
egress control.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
# Internal source workspace
|
|
45
|
+
|
|
46
|
+
This workspace is not the public npm package. It contains readable source,
|
|
47
|
+
tests, and release tooling. The public package is generated into
|
|
48
|
+
`release-public/` as `@letterblack/lbe-sdk`.
|
|
49
|
+
|
|
50
|
+
Every AI action passes through a local gate before it can execute.
|
|
51
|
+
|
|
52
|
+
**Local-first execution governance for AI agents.**
|
|
53
|
+
Deterministic execution. Workspace context. Audit. Rollback.
|
|
54
|
+
|
|
55
|
+
Everything runs on your machine. No cloud required.
|
|
56
|
+
|
|
57
|
+
**Distribution model:** this private package owns source truth. The public SDK
|
|
58
|
+
package ships only the generated public surface: bundled/minified `dist/`
|
|
59
|
+
entrypoints, `types.d.ts`, README, default policy template, and the WASM runtime.
|
|
60
|
+
LBE is SDK-only and local-only. It does not ship an MCP server, daemon, HTTP
|
|
61
|
+
API, hosted service, deployment infrastructure, or companion platform.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Private/Public Release Contract
|
|
66
|
+
|
|
67
|
+
Do internal development here:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npm install
|
|
71
|
+
npm run lint
|
|
72
|
+
npm test
|
|
73
|
+
npm run pack:check
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Build the public SDK package here:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npm run build:engine
|
|
80
|
+
npm run validate:all
|
|
81
|
+
npm run verify:public-sdk
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Before publishing, install the generated tarball in a clean temporary project
|
|
85
|
+
and exercise the supported CLI workflow (`lbe init`, `lbe status`, `lbe policy`,
|
|
86
|
+
and `lbe enforce`). Publish only from `release-public/` after that test passes.
|
|
87
|
+
|
|
88
|
+
### Public repository release
|
|
89
|
+
|
|
90
|
+
`../.github/workflows/release-public.yml` publishes only the generated
|
|
91
|
+
`release-public/` tree to `Letterblack0306/LetterBlack-Sentinel`; it never
|
|
92
|
+
pushes this private source repository, its history, or its internal files.
|
|
93
|
+
|
|
94
|
+
Set `PUBLIC_REPO_TOKEN` and `NPM_TOKEN` as Actions secrets in this repository.
|
|
95
|
+
The former needs Contents read/write permission to the public repository; the
|
|
96
|
+
latter needs npm publish permission for `@letterblack/lbe-sdk`. Push a matching
|
|
97
|
+
`v<package-version>` tag from current `main` to release automatically, or run
|
|
98
|
+
the workflow manually with the same tag value. The workflow verifies the
|
|
99
|
+
artifact and tests first, publishes npm, replaces the public repository working
|
|
100
|
+
tree with the release artifact, then creates its tag and GitHub release. It
|
|
101
|
+
blocks release unless the checked-out commit is the current `main` head.
|
|
102
|
+
|
|
103
|
+
## Source authority
|
|
104
|
+
|
|
105
|
+
Only `main` in the primary working tree is a source of truth. Do not develop,
|
|
106
|
+
commit, validate, or push from feature branches, detached HEADs, or linked Git
|
|
107
|
+
worktrees. Run `npm run hooks:install` after cloning; see
|
|
108
|
+
[`docs/governance/mainhead.md`](docs/governance/mainhead.md) for the blocker
|
|
109
|
+
rules.
|
|
110
|
+
|
|
111
|
+
Publish only from:
|
|
112
|
+
|
|
113
|
+
```text
|
|
114
|
+
release-public/
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The generated public package must not contain:
|
|
118
|
+
|
|
119
|
+
```text
|
|
120
|
+
src/core/
|
|
121
|
+
src/adapters/
|
|
122
|
+
src/cli/
|
|
123
|
+
scripts/
|
|
124
|
+
test/
|
|
125
|
+
keys/
|
|
126
|
+
data/
|
|
127
|
+
node_modules/
|
|
128
|
+
*.map
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Install
|
|
134
|
+
|
|
135
|
+
Internal source workspace:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
npm install @letterblack/lbe-core
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Public users install:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
npm install @letterblack/lbe-sdk
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Requires Node.js ≥ 20.9.0.
|
|
148
|
+
|
|
149
|
+
The current preview uses the audited JavaScript governance engine. The commercial
|
|
150
|
+
runtime target is a compiled engine loaded locally through WASM, with the same
|
|
151
|
+
public SDK and CLI surface.
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## After install
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
npx lbe execute --input input.json
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
`input.json` must contain a signed LBE execution request. You can also pipe the
|
|
162
|
+
same JSON request through stdin:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
cat input.json | npx lbe execute
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Quick Start
|
|
171
|
+
|
|
172
|
+
```js
|
|
173
|
+
import crypto from 'node:crypto';
|
|
174
|
+
import { execute } from '@letterblack/lbe-sdk';
|
|
175
|
+
|
|
176
|
+
const buildRequest = (name, payload = {}) => ({
|
|
177
|
+
version: '1.0',
|
|
178
|
+
request_id: crypto.randomUUID(),
|
|
179
|
+
timestamp: Math.floor(Date.now() / 1000),
|
|
180
|
+
actor: { id: 'agent:local', role: 'agent' },
|
|
181
|
+
intent: { type: 'command', name, payload },
|
|
182
|
+
context: { workspace: process.cwd(), env: {}, history: [] },
|
|
183
|
+
constraints: { policy_mode: 'strict', timeout_ms: 5000 },
|
|
184
|
+
auth: { signature: 'provided-by-host', nonce: crypto.randomUUID() }
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const output = execute(JSON.stringify(buildRequest('status')));
|
|
188
|
+
console.log(JSON.parse(output));
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
The public contract is `execute(input: string): string`. This repo still
|
|
192
|
+
contains the private source package and its internal SDK helpers below, but the
|
|
193
|
+
published surface is the generated `@letterblack/lbe-sdk` package.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Add what you need
|
|
198
|
+
|
|
199
|
+
**Audit — see exactly what changed and when:**
|
|
200
|
+
```js
|
|
201
|
+
const sb = sandbox('./workspace', { audit: true });
|
|
202
|
+
// appends to ~/.lbe/workspaces/<id>/audit.log.jsonl
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Rollback — undo failed writes automatically:**
|
|
206
|
+
```js
|
|
207
|
+
const sb = sandbox('./workspace', { audit: true, rollback: true });
|
|
208
|
+
// backs up before every write, restores on failure
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**State stays local** — all runtime state (audit logs, nonce store, backups) goes to
|
|
212
|
+
`~/.lbe/workspaces/<workspace-id>/` by default. Never inside your project, never sent anywhere.
|
|
213
|
+
|
|
214
|
+
To keep state inside the project instead (for team sharing):
|
|
215
|
+
```js
|
|
216
|
+
const sb = sandbox('./workspace', { state: 'workspace' });
|
|
217
|
+
// writes to .lbe/ inside your project root
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Integration boundary
|
|
223
|
+
|
|
224
|
+
LBE is embedded directly in the user's existing application through
|
|
225
|
+
`execute(input)` or `createLBE().execute()`. It requires no companion service
|
|
226
|
+
or separate runtime system. The calling code submits structured action requests
|
|
227
|
+
and remains responsible for giving the SDK-governed path exclusive authority.
|
|
228
|
+
|
|
229
|
+
The Model Context Protocol integration was removed because an MCP server only
|
|
230
|
+
offers LBE as an optional tool; an agent host with native tools can act outside
|
|
231
|
+
the governed execution boundary.
|
|
232
|
+
See [`docs/decisions/ADR-001-remove-mcp-execution-surface.md`](docs/decisions/ADR-001-remove-mcp-execution-surface.md).
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Agent Safe Execution
|
|
237
|
+
|
|
238
|
+
LBE Agent Safe Execution intercepts Node.js file and shell actions before they mutate the workspace.
|
|
239
|
+
|
|
240
|
+
Choose the integration path that matches how your agent runs:
|
|
241
|
+
|
|
242
|
+
### 1. Agents you own — direct integration at the dispatch point
|
|
243
|
+
|
|
244
|
+
If you control the agent's dispatch loop, integrate `lbe-exec` at the point where actions are issued. No preload hook needed — LBE governs each action directly through the SDK.
|
|
245
|
+
|
|
246
|
+
```js
|
|
247
|
+
import { createLocalExecutor } from '@letterblack/lbe-exec';
|
|
248
|
+
|
|
249
|
+
const lbe = createLocalExecutor({ rootDir: process.cwd(), mode: 'enforce' });
|
|
250
|
+
|
|
251
|
+
await lbe.writeFile('src/output.ts', content);
|
|
252
|
+
await lbe.deleteFile('src/old.ts');
|
|
253
|
+
await lbe.runShell('npm', ['test']);
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Every call passes through the full 7-gate pipeline: local policy, key lifecycle, signature, rate limit, nonce, and audit.
|
|
257
|
+
|
|
258
|
+
### 2. Node agents you launch — `run-node`
|
|
259
|
+
|
|
260
|
+
Use `run-node` to launch any Node.js agent under the preload hook. The hook patches `fs` and `child_process` before the agent's entry module runs — including actions taken by the agent's own npm dependencies.
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
npx lbe-exec run-node ./agent.js
|
|
264
|
+
npx lbe-exec run-node --mode enforce ./agent.js
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Patched APIs: `fs.writeFile`, `fs.writeFileSync`, `fs.rm`, `fs.rmSync`, `fs.unlink`, `fs.unlinkSync`, `fs.rename`, `fs.renameSync`, `fs.promises.*` variants, `child_process.spawn`, `spawnSync`, `exec`, `execSync`.
|
|
268
|
+
|
|
269
|
+
### 3. Existing npm scripts — `lbe-exec npm`
|
|
270
|
+
|
|
271
|
+
Inject the hook into any npm script via `NODE_OPTIONS`:
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
npx lbe-exec npm run agent
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
> **Note:** `NODE_OPTIONS`-based injection is less reliable than `run-node`. Some runtimes strip or override `NODE_OPTIONS`. Use `run-node` when the agent entry point is known.
|
|
278
|
+
|
|
279
|
+
### 4. Opaque or external agents — sandbox mode (future)
|
|
280
|
+
|
|
281
|
+
For agents that run outside Node.js, inside containers, or as opaque binaries — sandbox mode is planned. Not available in this release.
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
### Init — non-destructive script injection
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
npx lbe-exec init
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Detects agent scripts in `package.json` and adds `:lbe` and `:lbe:enforce` variants alongside them. Originals are never overwritten.
|
|
292
|
+
|
|
293
|
+
Input:
|
|
294
|
+
```json
|
|
295
|
+
{ "scripts": { "agent": "node ./src/agent.js" } }
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Output:
|
|
299
|
+
```json
|
|
300
|
+
{
|
|
301
|
+
"scripts": {
|
|
302
|
+
"agent": "node ./src/agent.js",
|
|
303
|
+
"agent:lbe": "lbe-exec run-node --mode observe ./src/agent.js",
|
|
304
|
+
"agent:lbe:enforce": "lbe-exec run-node --mode enforce ./src/agent.js",
|
|
305
|
+
"lbe:status": "lbe-exec status",
|
|
306
|
+
"lbe:audit": "lbe-exec audit"
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Status and audit
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
npx lbe-exec status # hook state, PID liveness, patched function table
|
|
315
|
+
npx lbe-exec audit # stream .lbe/events.jsonl as a formatted table
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
`status` reads `.lbe/runtime/hook-status.json` written by the hook at preload time and checks PID liveness in a separate process — no shared memory required.
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## Full governance
|
|
323
|
+
|
|
324
|
+
For teams, compliance workflows, or named actors with distinct permission sets:
|
|
325
|
+
|
|
326
|
+
```js
|
|
327
|
+
import { createLBE, generateKeyPair, createKeyStore } from '@letterblack/lbe-core';
|
|
328
|
+
|
|
329
|
+
const { secretKey, publicKey } = generateKeyPair();
|
|
330
|
+
|
|
331
|
+
const lbe = createLBE({
|
|
332
|
+
secretKey,
|
|
333
|
+
keyId: 'prod-key',
|
|
334
|
+
keyStore: createKeyStore({ publicKey, keyId: 'prod-key' }),
|
|
335
|
+
policy: {
|
|
336
|
+
version: 1,
|
|
337
|
+
default: 'DENY',
|
|
338
|
+
requesters: {
|
|
339
|
+
'agent:writer': {
|
|
340
|
+
allowCommands: ['write_file', 'read_file'],
|
|
341
|
+
allowAdapters: ['file'],
|
|
342
|
+
filesystem: {
|
|
343
|
+
roots: ['./output/'],
|
|
344
|
+
denyPatterns: ['*.key', '*.env']
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
const result = await lbe.execute({
|
|
352
|
+
actor: 'agent:writer',
|
|
353
|
+
intent: 'write_file',
|
|
354
|
+
target: './output/report.md',
|
|
355
|
+
content: '# Report\n',
|
|
356
|
+
transaction: { backup: true, rollbackOnFailure: true, audit: true }
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
console.log(result.ok); // true | false
|
|
360
|
+
console.log(result.stage); // 'executed' | 'validate' | 'invariant_gate' | ...
|
|
361
|
+
console.log(result.commandId); // matches audit log entry
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
Zero-config alternative — auto-generates keys and policy for `rootDir`:
|
|
365
|
+
|
|
366
|
+
```js
|
|
367
|
+
const lbe = createLBE({ rootDir: process.cwd() });
|
|
368
|
+
await lbe.writeFile('data/output.txt', 'result\n');
|
|
369
|
+
const text = await lbe.readFile('data/output.txt');
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
## How it works
|
|
375
|
+
|
|
376
|
+
```
|
|
377
|
+
Agent
|
|
378
|
+
│ proposes intent
|
|
379
|
+
▼
|
|
380
|
+
Invariant Gate ── key present? policy signed? keyStore loaded?
|
|
381
|
+
│
|
|
382
|
+
▼
|
|
383
|
+
7-Gate Validation
|
|
384
|
+
├── schema
|
|
385
|
+
├── key lifecycle
|
|
386
|
+
├── timestamp skew
|
|
387
|
+
├── signature
|
|
388
|
+
├── rate limit
|
|
389
|
+
├── nonce replay
|
|
390
|
+
└── policy (deny-by-default)
|
|
391
|
+
│
|
|
392
|
+
▼ all gates pass
|
|
393
|
+
Adapter ── file | shell | noop
|
|
394
|
+
│
|
|
395
|
+
▼
|
|
396
|
+
Audit Log ── SHA-256 hash-chained JSONL, append-only
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
The controller is the only authority. Adapters execute — they do not decide.
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## Public SDK surface
|
|
404
|
+
|
|
405
|
+
The published package (`@letterblack/lbe-sdk`) exposes one function:
|
|
406
|
+
|
|
407
|
+
```ts
|
|
408
|
+
export function execute(input: string): string;
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
`input` is a JSON-serialized `LBEExecuteInput`. The return value is a
|
|
412
|
+
JSON-serialized `LBEExecuteOutput`. Full type definitions are in `types.d.ts`.
|
|
413
|
+
|
|
414
|
+
```ts
|
|
415
|
+
interface LBEExecuteOutput {
|
|
416
|
+
ok: boolean;
|
|
417
|
+
result: { type: 'allowed' | 'denied' | 'error'; action: string; data: Record<string, unknown> };
|
|
418
|
+
policy: { decision: 'allow' | 'deny' | 'escalate'; reason: string; rules: string[] };
|
|
419
|
+
trace: { id: string; steps: unknown[]; hash: string };
|
|
420
|
+
error: null | { code: string; message: string };
|
|
421
|
+
}
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
## Internal source API (private package only)
|
|
427
|
+
|
|
428
|
+
These helpers exist in `@letterblack/lbe-core` (this private package) for
|
|
429
|
+
repository development and testing. They are **not** exported from the public
|
|
430
|
+
`@letterblack/lbe-sdk` package.
|
|
431
|
+
|
|
432
|
+
### `sandbox(root, opts?)` → `{ write, read, patch, lbe }`
|
|
433
|
+
|
|
434
|
+
| Option | Default | Description |
|
|
435
|
+
|---|---|---|
|
|
436
|
+
| `audit` | `false` | Append to audit log on every operation |
|
|
437
|
+
| `rollback` | `false` | Back up before write; restore on failure |
|
|
438
|
+
| `state` | `'local'` | `'local'` → `~/.lbe/` · `'workspace'` → `.lbe/` inside project |
|
|
439
|
+
|
|
440
|
+
### `createLBE(options)` → `{ execute, writeFile, readFile, exportLogs }`
|
|
441
|
+
|
|
442
|
+
| Option | Type | Description |
|
|
443
|
+
|---|---|---|
|
|
444
|
+
| `rootDir` | `string` | Auto-configure keys, policy, and state for this directory |
|
|
445
|
+
| `secretKey` | `string` | Ed25519 secret key (base64) |
|
|
446
|
+
| `keyId` | `string` | Key identifier |
|
|
447
|
+
| `policy` | `object` | Inline policy object |
|
|
448
|
+
| `state` | `string` | `'local'` (default) · `'workspace'` · `{ adapter }` |
|
|
449
|
+
| `logLevel` | `string` | `DEBUG` · `INFO` · `WARN` · `ERROR` |
|
|
450
|
+
|
|
451
|
+
### Additional exports
|
|
452
|
+
|
|
453
|
+
```js
|
|
454
|
+
import { AVAILABLE_ADAPTERS } from '@letterblack/lbe-core/adapters';
|
|
455
|
+
// → ['noop', 'shell', 'file']
|
|
456
|
+
|
|
457
|
+
import {
|
|
458
|
+
validateCommand,
|
|
459
|
+
appendAudit,
|
|
460
|
+
createBackup, restoreBackup,
|
|
461
|
+
signEd25519, verifyEd25519, generateKeyPair,
|
|
462
|
+
createLogger, deepFreeze,
|
|
463
|
+
checkInvariants, assertInvariants, InvariantGateError
|
|
464
|
+
} from '@letterblack/lbe-core';
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
## CLI
|
|
468
|
+
|
|
469
|
+
| Command | Description |
|
|
470
|
+
|---|---|
|
|
471
|
+
| `npx lbe execute --input input.json` | Execute one JSON request through the public runtime |
|
|
472
|
+
| `cat input.json \| npx lbe execute` | Execute one JSON request from stdin |
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
## Security model
|
|
477
|
+
|
|
478
|
+
- **Ed25519 signatures** — every proposal is signed; the controller verifies before any gate runs
|
|
479
|
+
- **Nonce replay protection** — each `commandId` is single-use; replays are hard-rejected
|
|
480
|
+
- **Rate limiting** — per-requester, configurable window
|
|
481
|
+
- **Timestamp skew guard** — rejects proposals outside ±10 minutes
|
|
482
|
+
- **Policy signature verification** — the policy file itself is signed; tampering fails the invariant gate
|
|
483
|
+
- **Immutable audit trail** — SHA-256 hash-chained JSONL; any deletion or edit is detectable
|
|
484
|
+
- **Atomic writes** — all state files use write-then-rename; no partial state
|
|
485
|
+
|
|
486
|
+
**What is never committed:**
|
|
487
|
+
`keys/secret.key` · `keys/*.key` · `config/keys.json` · `config/policy.sig.json` · `data/`
|
|
488
|
+
|
|
489
|
+
**Scope — what this hardens against:**
|
|
490
|
+
Accidental writes outside allowed roots. Replay attacks. Policy drift. Audit log tampering. Rollback on failure.
|
|
491
|
+
|
|
492
|
+
**Not in scope:**
|
|
493
|
+
Kernel-level process isolation. Network egress control. Multi-tenant workload separation.
|
|
494
|
+
This SDK does not sandbox the agent process — it governs the actions that agent routes through the SDK.
|
|
495
|
+
|
|
496
|
+
---
|
|
497
|
+
|
|
498
|
+
## Changelog
|
|
499
|
+
|
|
500
|
+
See [`CHANGELOG.md`](CHANGELOG.md).
|
|
501
|
+
|
|
502
|
+
---
|
|
503
|
+
|
|
504
|
+
## License
|
|
505
|
+
|
|
506
|
+
MIT — LetterBlack 2026
|