Skip to content

feat(agentDir): enforce registry permission manifest at load and run time - #386

Merged
LinuxDevil merged 1 commit into
mainfrom
fix/lou-s7-registry-manifest
Oct 4, 2026
Merged

LinuxDevil merged 1 commit into
mainfrom
fix/lou-s7-registry-manifest

Conversation

@LinuxDevil

Copy link
Copy Markdown
Owner

Closes #272

What

Extends the M7a registry permission-manifest work from install-time static enforcement to load-time and run-time enforcement. The lousho-registry.json receipt written by lousho add is no longer ambient authorization — it is evidence tied to the installed artifact, re-verified against the files actually loaded.

How

  • src/agentDir/registryEnforce.ts (new):
    • verifyReceipt(dir) reads the receipt and re-hashes every recorded file (sha256, same scheme as addReceipt.ts). An item whose files changed or went missing is unattested; a missing/unparseable receipt is reported, never thrown.
    • registryWarnings() produces one warning per unattested item naming the item and the modified/missing files.
    • confineToolsToReceipt() wraps every tool whose source file a receipt item owns so calls run inside the manifest accepted at install:
      • exec: true or needsApproval: true items — and any unattested item — always wait for approval (a tool's own deny still denies; approval is the channel that surfaces stale code).
      • fetch while the tool runs is limited to the declared network host patterns (matchesHost); an item with no network cannot fetch at all.
      • process.env while the tool runs shows only the declared env names.
      • A requiresSandbox tool gets a narrowed SandboxAdapter: run() refused without exec, writeFile() without filesystem: "write", and command env = the SDK's non-secret base plus declared variables only (commandEnv).
    • Ambient fetch/process.env guards are scoped per call via AsyncLocalStorage, so unrelated code and the SDK's own env use are untouched. Async-generator (isPartialStream) results are confined per next().
  • loadAgentDir.ts: resolveWith verifies the receipt before loading tools, prints the warnings, confines the loaded tools, and reports the result on manifest.registry (new RegistryStatus/RegistryItemStatus/Attestation types, exported via agentDir/index.ts).
  • Loading is never refused — owner edits are allowed; a stale receipt just cannot silently widen authority. The envelope stays at the last accepted manifest until lousho add <name> --overwrite re-attests the current files.

Adversarial case covered

Install an item declaring no network/exec, then edit its tool file to call fetch and a sandboxed run(): the load reports the item unattested, its calls now require approval, the fetch is refused (declares no network access), and sandbox.run() throws without declared exec.

Limits (documented)

In-process guards intercept fetch and process.env only — not http/net/axios, the filesystem, module-scope captures, or a container's own network policy. The manifest is not a sandbox; this is stated in docs/registry.md.

Tests

  • New src/agentDir/registryEnforce.test.ts: 14 tests — receipt parse/read errors, attested vs modified/missing reporting, valid receipt doesn't block load, approval for exec/needsApproval/unattested items (incl. own deny preserved), fetch allow/refuse, env filtering, sandbox adapter narrowing, and the modified-after-install adversarial flow.
  • Targeted: registryEnforce.test.ts (14) + add.test.ts (46) + loadAgentDir.test.ts (18) = 78 pass; broader related run earlier: 100 pass / 8 files.
  • npm run lint ✅ npm run typecheck ✅ npm run build ✅ docs:verify-snippets --skip-build ✅ (258 snippets) docs:llms regenerated.
  • Note: npm run typecheck:tests has pre-existing failures across unrelated test files on main; the new test file itself is clean.

Docs

  • docs/registry.md: permission-manifest section now covers load-time integrity, runtime approval/network/env/sandbox enforcement, the re-attestation path, and the confinement's limits.
  • docs/agent-directories.md: manifest.registry on resolveAgentDir().
  • CHANGELOG.md: Unreleased Security entry; llms.txt/llms-full.txt regenerated.

…time (#272)

The lousho add receipt (M7a) is now verified when an agent directory
loads and enforced while its tools run, instead of being install-time
documentation only.

- verifyReceipt re-hashes every receipt file; an item with edited or
  missing files is 'unattested': a warning names the item and files,
  and manifest.registry reports the same. Loading is never refused -
  the files are the owner's to edit.
- confineToolsToReceipt binds each receipt item's tools to the manifest
  accepted at install: exec/needsApproval items - and any unattested
  item - always ask for approval (a tool's own deny still denies);
  fetch is limited to the declared network hosts; process.env shows
  only the declared env names; a requiresSandbox tool gets a narrowed
  adapter (run() refused without exec, writeFile() without
  filesystem write, command env of the non-secret base plus declared
  variables only). Ambient fetch/env guards run under AsyncLocalStorage
  so unrelated code is untouched.
- The envelope stays as narrow as the last accepted manifest until
  'lousho add <name> --overwrite' attests the current files, so a
  post-install edit adding network or exec use is surfaced and refused.

Closes #272
@LinuxDevil
LinuxDevil force-pushed the fix/lou-s7-registry-manifest branch from 76eeb9a to 12d829a Compare October 4, 2026 09:03
@LinuxDevil
LinuxDevil merged commit 8c96009 into main Oct 4, 2026
1 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Registry: enforce the installed permission manifest at load and run time (follow-up to M7a)

2 participants