Skip to content

feat(plugins): add memlawb as an opt-in built-in plugin - #2206

Draft
beardthelion wants to merge 2 commits into
Gitlawb:mainfrom
beardthelion:feat/memlawb-plugin
Draft

beardthelion wants to merge 2 commits into
Gitlawb:mainfrom
beardthelion:feat/memlawb-plugin

Conversation

@beardthelion

@beardthelion beardthelion commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds memlawb as an opt-in built-in plugin, disabled by default, contributing one stdio MCP server and nothing else: no skill, no hook, no prompting.
  • memlawb is a zero-knowledge memory backend; its MCP server gives an agent durable end-to-end-encrypted memory. The passphrase is the encryption key and never reaches the memlawb server.
  • The plugin ships no prompting on purpose. The memdir pipeline and team memory are untouched, and nothing here competes with them for when a fact gets written.

I read CONTRIBUTING.md and AGENTS.md before opening this.

Impact

  • User: /plugin gains a memlawb entry. Enabling it needs the memlawb CLI 0.1.0 or newer on PATH and four environment variables. Nothing changes for anyone who leaves it off, which is everyone by default.
  • Developer: one new bundled plugin and its test, plus one line in initBuiltinPlugins. No existing behaviour is modified.

Why the passphrase is a path, not a value

This is the part worth reviewing. Every value in the server's env is a ${VAR} reference, and the passphrase is referenced as a file path via MEMLAWB_PASSPHRASE_FILE.

The reason is specific to this codebase rather than general caution. src/services/mcp/client.ts builds a stdio child's environment as { ...subprocessEnv(), ...server env }, and subprocessEnv() returns the parent's own environment. So a passphrase exported so openclaude can expand it into memlawb's config is equally readable by every other MCP server the user runs, not just this one.

A path still rides that same shared environment. The secret no longer does. That narrows the exposure from "every configured MCP server has your passphrase" to "every configured MCP server knows where the file is", which is a real improvement and is not isolation. The plugin's doc comment says exactly that rather than implying more.

Only the path form is declared, and not merely to avoid re-opening what this closes: memlawb checks MEMLAWB_PASSPHRASE for unexpanded template text, and this host leaves an unset reference as its own literal, so a correctly configured user (file exported, variable deliberately unset) would be handed template text and refused at startup. Declaring both is a hard break of the intended setup, not a fallback.

MEMLAWB_PASSPHRASE_FILE is a memlawb-side input added for this integration, which is why the description names the minimum version. An older binary reads only MEMLAWB_PASSPHRASE and fails saying no passphrase is set, which points a user at their own config rather than at a stale install.

Testing

  • I ran the required local preflight.

Exact commands and results:

bun install --frozen-lockfile              ok
bun run check                              1 pass, 0 fail
bun run typecheck                          3 errors, all pre-existing (see below)
bun run typecheck:type-tests               passed, 10 files
node bin/openclaude --version              0.30.0 (OpenClaude)
bun run test:provider                      1630 tests, 0 fail
npm run test:provider-recommendation       152 pass, 0 fail
bun run security:pr-scan                   no suspicious additions found

Focused tests: bun test src/plugins/bundled/ (10 pass), and bun test src/plugins/ src/utils/plugins/ src/services/mcp/ (257 pass, 23 files).

Verified pre-existing failures: bun run typecheck reports three errors, in src/tools/AgentTool/AgentTool.teammateModel.test.ts:457 and src/utils/permissions/permissions.test.ts:695,713. I confirmed these by running the same typecheck on a clean origin/main worktree, which produces the identical three. They are in files this PR does not touch.

Every guard in the new test was removed once and the named test observed red: default-enabled flipped, a skill added, a hook added, the command and args changed, a second server added, the registration removed, the passphrase written as a literal, declared as both forms, and pointed at the wrong variable.

The absence test ("the passphrase reaches no configured server") carries two positive controls, because without them it passes against a check that finds nothing: the service key is present in memlawb's resolved env, and a server that deliberately declares the passphrase does receive it. Its limit is recorded in the test body: it reads resolved plugin config and cannot see the spawn-time spread, so what it proves is that this plugin does not put the secret there.

Notes

  • Provider path tested: none touched. This PR adds no provider code.
  • No screenshots: nothing renders differently beyond the plugin's entry in the existing /plugin list.
  • Known limitation, not automated: two scenarios need a recorded human session and nothing here stands in for them. First, that with the plugin enabled the memdir daily log is still written and memories still surface, and that disabling leaves no memlawb artifact behind. Second, that a durable cross-machine preference saved in a session lands in memlawb, with a session-log fact as the control that does not. I can prove the wiring; I cannot prove the model's behaviour without running it.
  • Draft until the memlawb release that carries MEMLAWB_PASSPHRASE_FILE is published, since the plugin is unusable before then.

Summary by CodeRabbit

  • New Features
    • Added an opt-in Memlawb integration as a built-in plugin.
    • Supports connecting to Memlawb through its MCP server with configurable URL, API key, namespace, and passphrase file settings.

Offers memlawb's MCP server, which gives an agent durable end-to-end-encrypted
memory, as a plugin that is off until someone turns it on. It contributes one
stdio server and nothing else: no skill, no hook, no prompting. That is the
point rather than an omission, because the memdir pipeline and team memory have
to keep working exactly as they do now.

Every value in the server's environment is a ${VAR} reference, never a literal.
The reason is specific to this codebase: a stdio child's environment is built as
{ ...subprocessEnv(), ...server env } in src/services/mcp/client.ts, and
subprocessEnv() returns the parent's own environment, so anything exported for
one MCP server is readable by all of them.

The passphrase is therefore referenced by PATH rather than by value, through
MEMLAWB_PASSPHRASE_FILE. The path still rides the same shared environment; the
secret no longer does. That narrows the exposure from "every configured MCP
server has your passphrase" to "every configured MCP server knows where it is",
which is a real improvement and is not isolation, and the plugin's comment says
so in those terms.

Only the path form is declared, and not merely because offering both would
re-open what this closes. memlawb checks MEMLAWB_PASSPHRASE for unexpanded
template text, and this host leaves an unset reference as its own literal, so a
correctly configured user with the file exported and the variable deliberately
unset would be handed template text and refused at startup. Declaring both is a
hard break of the intended setup, not a fallback.

The absence test asserts the passphrase reaches no configured server and carries
two positive controls: the service key does reach memlawb's resolved
environment, and a server that deliberately declares the passphrase does receive
it, so the assertion can fail. Its limit is recorded in the test: it reads
resolved plugin config and cannot see the spawn-time spread, so what it proves
is that this plugin does not put the secret there.

Two scenarios remain manual and are not stood in for by anything here: that
enabling the plugin leaves the memdir log and its recall working and disabling
it leaves nothing behind, and that a durable cross-machine preference lands in
memlawb while a session-log fact does not.

Signed-off-by: beardthelion <56458543+beardthelion@users.noreply.github.com>
The plugin references MEMLAWB_PASSPHRASE_FILE, which older memlawb builds do not
read. Those builds refuse to start saying no passphrase is set, which points a
user at their own configuration rather than at an out-of-date binary, so the
description now says which version reads it. zero's equivalent already printed a
minimum; this brings the two integrations in line.

Signed-off-by: beardthelion <56458543+beardthelion@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Essentials

Run ID: f9e360b5-7394-40be-923b-2b34242503f6

📥 Commits

Reviewing files that changed from the base of the PR and between 0ea8eef and 4db48d7.

📒 Files selected for processing (3)
  • src/plugins/bundled/index.ts
  • src/plugins/bundled/memlawb.test.ts
  • src/plugins/bundled/memlawb.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.

📜 Recent review details
🧰 Additional context used
📓 Path-based instructions (2)
Review tests for meaningful coverage of the changed behavior, isolation of global/env/config state, async cleanup, fake timers, provider profile leaks, and Windows-compatible assumptions.

⚙️ CodeRabbit configuration file

Files:

  • src/plugins/bundled/memlawb.test.ts
Apply the OpenClaude maintainer review rubric from AGENTS.md.

⚙️ CodeRabbit configuration file

Files:

  • src/plugins/bundled/index.ts
  • src/plugins/bundled/memlawb.ts
  • src/plugins/bundled/memlawb.test.ts
🪛 ast-grep (0.45.2)
src/plugins/bundled/memlawb.test.ts

[error] 31-31: Recursive/iterative merge copies attacker-controllable keys from a source object into a target via a computed property assignment without rejecting dangerous keys, allowing prototype pollution. Skip or block "proto", "constructor", and "prototype" keys (e.g. if (key === "__proto__" || key === "constructor" || key === "prototype") continue;), use a null-prototype object (Object.create(null)), or use a safe merge utility instead.
Context: for (const name of TOUCHED) saved[name] = process.env[name]
Note: [CWE-1321] Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution').

(prototype-pollution-recursive-merge-typescript)


[error] 43-46: Recursive/iterative merge copies attacker-controllable keys from a source object into a target via a computed property assignment without rejecting dangerous keys, allowing prototype pollution. Skip or block "proto", "constructor", and "prototype" keys (e.g. if (key === "__proto__" || key === "constructor" || key === "prototype") continue;), use a null-prototype object (Object.create(null)), or use a safe merge utility instead.
Context: for (const name of TOUCHED) {
if (saved[name] === undefined) delete process.env[name]
else process.env[name] = saved[name]
}
Note: [CWE-1321] Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution').

(prototype-pollution-recursive-merge-typescript)

🔇 Additional comments (3)
src/plugins/bundled/memlawb.ts (1)

1-48: LGTM!

Also applies to: 50-73

src/plugins/bundled/memlawb.test.ts (1)

1-196: LGTM!

src/plugins/bundled/index.ts (1)

18-25: LGTM!


📝 Walkthrough

Walkthrough

Adds an opt-in bundled Memlawb plugin that runs memlawb mcp, forwards four environment references, supports passphrase-file configuration, and registers during built-in initialization. Tests cover metadata, secret resolution, validation, and startup wiring.

Changes

Memlawb plugin

Layer / File(s) Summary
Plugin definition and configuration contract
src/plugins/bundled/memlawb.ts, src/plugins/bundled/memlawb.test.ts
Defines the disabled-by-default memlawb mcp stdio server and documents version 0.1.0, environment references, and passphrase-file configuration.
Environment resolution and validation
src/plugins/bundled/memlawb.test.ts
Tests expansion of URL, API key, namespace, and passphrase-file values. Tests reject missing passphrase-file configuration and prevent implicit raw passphrase exposure.
Built-in registration
src/plugins/bundled/index.ts, src/plugins/bundled/memlawb.test.ts
Registers Memlawb during initBuiltinPlugins() and verifies the plugin appears with existing built-ins.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 4db48

This adds an opt-in Memlawb MCP plugin with passphrase-file configuration and leaves existing users unaffected unless they enable it. No current merge-readiness risk remains.

🚥 Pre-merge checks | ✅ 6 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 3 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (6 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise, scoped, and accurately describes the addition of the opt-in Memlawb built-in plugin.
Description check ✅ Passed The description covers the required Summary, Impact, Testing, and Notes sections. It explains the implementation, security rationale, test results, pre-existing failures, and known limitations.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Risk Surface Disclosed ✅ Passed The PR clearly discloses the risk surface. The changed code adds an opt-in stdio MCP plugin with defaultEnabled: false, one memlawb mcp server, four environment references, and no skill or hook. T…
No Hidden Policy Change ✅ Passed PASS. The PR changes only the bundled-plugin registration, the new memlawb plugin, and its tests. The plugin is explicitly opt-in (defaultEnabled: false), adds one stdio MCP server, and adds no skil…
  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@kevincodex1

Copy link
Copy Markdown
Member

wow cool thanks for this @beardthelion

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.

2 participants