brainllm 12.7.1 → 12.8.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/README.md +22 -12
- package/SECURITY.md +4 -2
- package/dist/index.js +670 -111
- package/manifest.json +4 -4
- package/package.json +5 -4
- package/server.json +5 -5
- package/skills/brainllm/SKILL.md +10 -8
- package/skills/brainllm/references/troubleshooting.md +4 -0
package/README.md
CHANGED
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
**Give Claude a memory that survives the session.**
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Self-hosted memory for LLMs in [TriliumNext Notes](https://github.com/TriliumNext/Trilium), structured by the server and editable by you, served over the [Model Context Protocol](https://modelcontextprotocol.io).
|
|
10
10
|
|
|
11
|
-
[**
|
|
11
|
+
[**Site**](https://miisodev.github.io/BrainLLM/) · [Connect to Claude](https://miisodev.github.io/BrainLLM/connect.html) · [How it works](https://miisodev.github.io/BrainLLM/how-it-works.html) · [Docs](https://miisodev.github.io/BrainLLM/docs.html) · [Privacy](https://miisodev.github.io/BrainLLM/privacy.html)
|
|
12
12
|
|
|
13
|
-
[](https://github.com/miisodev/BrainLLM/releases)
|
|
14
14
|
[](https://github.com/miisodev/BrainLLM/actions/workflows/ci.yml)
|
|
15
15
|
[](./LICENSE)
|
|
16
16
|
[](https://bun.sh)
|
|
@@ -35,7 +35,7 @@ Anyone running an LLM as a long-running collaborator, not a one-shot Q&A tool
|
|
|
35
35
|
|
|
36
36
|
### From experiment to open source
|
|
37
37
|
|
|
38
|
-
BrainLLM began in February 2026 as a personal experiment: could an LLM operate a real, self-hosted
|
|
38
|
+
BrainLLM began in February 2026 as a personal experiment: could an LLM operate a real, self-hosted memory reliably enough to be trusted as its own memory — orienting, writing, connecting, and closing sessions without a human doing the filing? Through sustained daily, production use the answer held. The design has settled, the failure modes have been found and fixed, and the project has graduated from experiment to something **efficient and stable enough to share** — so it's now open source. It still runs the author's own sessions every day; what you're reading is the same code, not a demo. It also still carries the shape of one person's daily use — see [Adapting to your environment](#adapting-to-your-environment) for exactly what that means and what to change.
|
|
39
39
|
|
|
40
40
|
### The core principle
|
|
41
41
|
|
|
@@ -199,15 +199,15 @@ The **full mode** tools deliberately break this convention — they are named af
|
|
|
199
199
|
|
|
200
200
|
### Tool permissions
|
|
201
201
|
|
|
202
|
-
Every tool declares whether it reads, writes, or destroys, so your client can group them and you can grant permission per group instead of per call. The current full-mode split is **
|
|
202
|
+
Every tool declares whether it reads, writes, or destroys, so your client can group them and you can grant permission per group instead of per call. Every tool also carries a human-readable title ("Edit a note", "Search the brain") for permission prompts and directory listings. The current full-mode split is **34 read-only · 31 write · 13 destructive** (core mode is **23 read-only · 17 write · 5 destructive**); `revise`, `label` and `connect` count as destructive because they can overwrite or remove what was there, which lets reads run unattended while anything that touches your brain still asks.
|
|
203
203
|
|
|
204
204
|
The classification lives in one reviewable table ([`src/annotations.ts`](./src/annotations.ts)) rather than scattered across registrations, and its default is deliberately unsafe-side: a tool missing from the table is treated as a **write**, never a read, and says so at startup. Three are worth knowing because they look like reads and aren't — `start()` creates today's diary and session stubs, `session()` runs the maintenance sweep, and `graph()` writes the rendered graph note.
|
|
205
205
|
|
|
206
|
-
### Core — universal verbs (
|
|
206
|
+
### Core — universal verbs (35)
|
|
207
207
|
|
|
208
208
|
| Group | Tools |
|
|
209
209
|
|---|---|
|
|
210
|
-
| Session lifecycle | `start` · `day` · `session` · `remarks` · `close` · `backup` |
|
|
210
|
+
| Session lifecycle | `start` · `day` · `session` · `remarks` · `close` · `backup` · `health` |
|
|
211
211
|
| Writing | `remember` · `diary` · `revise` · `split` · `resolve` · `withdraw` · `recover` |
|
|
212
212
|
| Reading & search | `read` · `recall` · `domain` · `brain` · `assembly` · `outline` · `inspect` · `diff` · `template` · `consistency` |
|
|
213
213
|
| Graph | `connect` · `explore` · `graph` |
|
|
@@ -222,7 +222,7 @@ The classification lives in one reviewable table ([`src/annotations.ts`](./src/a
|
|
|
222
222
|
|
|
223
223
|
The raw ETAPI, one tool per Trilium primitive — notes, attributes, branches, revisions, attachments, calendar, system. Brain-agnostic and guard-free: an edge-case fallback for what core can't express (precise raw queries, code/canvas/mermaid notes, branch surgery, revision recovery).
|
|
224
224
|
|
|
225
|
-
The complete operational reference is [`skills/brainllm/SKILL.md`](./skills/brainllm/SKILL.md); per-topic deep dives live in [`skills/brainllm/references/`](./skills/brainllm/references/). Beyond that, the **
|
|
225
|
+
The complete operational reference is [`skills/brainllm/SKILL.md`](./skills/brainllm/SKILL.md); per-topic deep dives live in [`skills/brainllm/references/`](./skills/brainllm/references/). Beyond that, the **architecture reference** explains how BrainLLM is built and why — every module, every tool's behaviour, the sweep internals, the idempotency matrix — and is kept current with each release rather than published in this repo; see [Support the project](#support-the-project) for how to get a copy.
|
|
226
226
|
|
|
227
227
|
---
|
|
228
228
|
|
|
@@ -243,7 +243,7 @@ The included [`Dockerfile`](./Dockerfile) builds and runs the HTTP connector (tw
|
|
|
243
243
|
|
|
244
244
|
### Branding
|
|
245
245
|
|
|
246
|
-
In HTTP mode BrainLLM serves its own icons — `/icon.png`, `/icon.svg`, sized variants, and `/favicon.ico` — and advertises them in the MCP handshake, so clients show the BrainLLM mark in your connector list and beside its tool calls.
|
|
246
|
+
In HTTP mode BrainLLM serves its own icons — `/icon.png`, `/icon.svg`, sized variants, and `/favicon.ico` — and advertises them in the MCP handshake together with its title, a one-line description and its website, so clients show the BrainLLM mark in your connector list and beside its tool calls. The handshake also carries short server instructions describing the five areas and the session shape. Everything brand-shaped (name, tagline, description, the mark's geometry) is defined once in [`src/brand.ts`](./src/brand.ts), and a unit test holds `package.json`, `manifest.json` and `server.json` to the same description.
|
|
247
247
|
|
|
248
248
|
Both halves of that are load-bearing. The spec tells clients to **verify icon URIs share the server's origin**, so icons hosted anywhere else are silently dropped. And a host that serves no favicon makes clients fall back to the *registrable domain's* — a BrainLLM at `brain.yourdomain.com` would show `yourdomain.com`'s site logo, which is worse than nothing because it looks intentional.
|
|
249
249
|
|
|
@@ -253,7 +253,7 @@ To use your own artwork, replace `public/BrainLLM.svg` and run `bun run icons`
|
|
|
253
253
|
|
|
254
254
|
Claude Code and `mcp-remote` send `MCP_AUTH_TOKEN` as a header and need nothing further. **The hosted Claude surfaces — claude.ai, Claude mobile, Cowork — cannot.** Their custom-connector UI offers OAuth or nothing; there is no field for a bearer token. So BrainLLM ships its own OAuth 2.1 authorization server. It serves **both** registration mechanisms, so every OAuth-capable client works:
|
|
255
255
|
|
|
256
|
-
- **Client ID Metadata Documents (CIMD)** — the registration-free mechanism MCP
|
|
256
|
+
- **Client ID Metadata Documents (CIMD)** — the registration-free mechanism adopted in the MCP `2025-11-25` specification; this is what Claude selects.
|
|
257
257
|
- **Dynamic Client Registration (RFC 7591)** — for clients that never grew CIMD support. opencode (MCP TS SDK ≤1.29) is the forcing case: without a `registration_endpoint` it refuses to authenticate at all.
|
|
258
258
|
|
|
259
259
|
Set one variable to turn it on:
|
|
@@ -273,9 +273,11 @@ What that turns on:
|
|
|
273
273
|
| `/.well-known/oauth-protected-resource` (+ `/mcp` variant) | RFC 9728 — names the authorization server |
|
|
274
274
|
| `/.well-known/oauth-authorization-server` (+ `/mcp` variant) | RFC 8414 — advertises CIMD support, DCR, and PKCE S256 |
|
|
275
275
|
| `/authorize` | Validates the client (CIMD document or registered id), shows the consent screen |
|
|
276
|
-
| `/token` | PKCE-verified code exchange, with rotating refresh tokens |
|
|
276
|
+
| `/token` | PKCE-verified code exchange, with rotating refresh tokens stored only as SHA-256 hashes |
|
|
277
277
|
| `/register` | RFC 7591 dynamic client registration — how opencode and other DCR-only clients connect |
|
|
278
278
|
|
|
279
|
+
The consent screen names the requesting client and the address it will return to, and warns when that address is your own computer (a loopback redirect, as Claude Code uses). MCP traffic is rate-limited per bearer token, with a looser per-IP bound, so clients that share an egress range (all of Claude's hosted traffic does) never exhaust each other's budget.
|
|
280
|
+
|
|
279
281
|
Both credentials work simultaneously — a static token from Claude Code and an OAuth token from claude.ai, against the same brain. Access tokens are signed JWTs bound to your server's resource URI, so a token minted for a different MCP server is rejected; the signing secret and refresh tokens persist beside `brainllm.json`, which on a container deploy means **putting `BRAINLLM_CONFIG` on a volume** — otherwise every redeploy invalidates every token.
|
|
280
282
|
|
|
281
283
|
For config persistence across redeploys, mount a volume on the **BrainLLM service** (not the Trilium service) and set `BRAINLLM_CONFIG` to a file path inside it:
|
|
@@ -341,12 +343,15 @@ index.ts ─→ tools.ts ─┬→ trilium.ts ETAPI client: bounded/retrying
|
|
|
341
343
|
├→ journal.ts daily Insights log generation (regenerate-in-place)
|
|
342
344
|
├→ time.ts timezone-correct now/today (BRAINLLM_TZ)
|
|
343
345
|
├→ rate-limit.ts bounded public HTTP budgets
|
|
346
|
+
├→ brand.ts name, description, mark, icons, server instructions
|
|
347
|
+
├→ cap.ts the 140k-character result cap
|
|
348
|
+
├→ diffing.ts block and table-cell diffs
|
|
344
349
|
├→ network-security.ts CIMD public-network and redirect checks
|
|
345
350
|
├→ bootstrap.ts five-area tree builder
|
|
346
351
|
└→ tools-*.ts per-area surface reads · full-mode raw ETAPI
|
|
347
352
|
```
|
|
348
353
|
|
|
349
|
-
Key properties: every write is sanitized for Trilium/CKEditor 5 compatibility (mutations reported back as `sanitized[]`); a revision snapshot precedes routine content mutation; every request carries the user's local time so Trilium stamps dates in the right day; core writes are idempotent or duplicate-guarded, while raw full-mode overwrites are explicit.
|
|
354
|
+
Key properties: no tool result exceeds 140,000 characters (a larger one is cut with a note naming the narrower read); every write is sanitized for Trilium/CKEditor 5 compatibility (mutations reported back as `sanitized[]`); a revision snapshot precedes routine content mutation; every request carries the user's local time so Trilium stamps dates in the right day; core writes are idempotent or duplicate-guarded, while raw full-mode overwrites are explicit.
|
|
350
355
|
|
|
351
356
|
## Development
|
|
352
357
|
|
|
@@ -355,6 +360,7 @@ bun run dev # hot-reload dev server
|
|
|
355
360
|
bun run build # bundle to dist/index.js
|
|
356
361
|
bun run test:unit # all unit tests (Bun discovery; works on Windows and CI)
|
|
357
362
|
bun run test:http # local auth/header smoke (no Trilium required)
|
|
363
|
+
BRAINLLM_LIVE_TOKEN=... bun run test:live # read-only smoke against a deployed server, over real MCP
|
|
358
364
|
bun run test # integration tests (requires a live Trilium instance)
|
|
359
365
|
bun run validate:manifest # MCPB schema/icon validation
|
|
360
366
|
bun run init # CLI bootstrap
|
|
@@ -392,6 +398,10 @@ Not sure where to start? Issues labeled `good first issue`, doc gaps, and the tr
|
|
|
392
398
|
|
|
393
399
|
Please do not report suspected vulnerabilities in a public issue. Follow the private reporting process in [`SECURITY.md`](./SECURITY.md); never include ETAPI tokens, OAuth credentials, or private brain content in a report.
|
|
394
400
|
|
|
401
|
+
## Privacy and support
|
|
402
|
+
|
|
403
|
+
BrainLLM collects nothing: no analytics, no telemetry, no account. Your notes stay in your Trilium, and sign-in records stay on your own server. The full [privacy policy](https://miisodev.github.io/BrainLLM/privacy.html) says what is stored where. For help, open a [GitHub issue](https://github.com/miisodev/BrainLLM/issues) or email miisodev@gmail.com.
|
|
404
|
+
|
|
395
405
|
---
|
|
396
406
|
|
|
397
407
|
## Support the project
|
package/SECURITY.md
CHANGED
|
@@ -6,8 +6,8 @@ Security fixes ship on the latest release line. Older versions may remain useful
|
|
|
6
6
|
|
|
7
7
|
| Version | Supported |
|
|
8
8
|
|---|---|
|
|
9
|
-
| 12.
|
|
10
|
-
| 12.
|
|
9
|
+
| 12.8.x | ✅ |
|
|
10
|
+
| 12.7.x and earlier | ❌ |
|
|
11
11
|
|
|
12
12
|
## Reporting a vulnerability
|
|
13
13
|
|
|
@@ -26,6 +26,8 @@ Please use a private channel until a fix and coordinated disclosure path are agr
|
|
|
26
26
|
|
|
27
27
|
Run the latest tagged release rather than an unreleased branch or a mutable dependency lock. For an HTTP deployment reachable outside a trusted network, set `MCP_AUTH_TOKEN`; configure `BRAINLLM_OWNER_PASSWORD` only when hosted OAuth clients require it. Keep the Trilium instance, ETAPI token, OAuth store, and `brainllm.json` on protected storage, and restrict network access at the platform edge.
|
|
28
28
|
|
|
29
|
+
Since 12.8.0 the OAuth store keeps refresh tokens only as SHA-256 hashes (a token issued earlier is accepted once and replaced by a hashed successor on its next refresh), MCP traffic is rate-limited per bearer token with a looser per-IP bound, and the consent screen shows where an approval will be sent and warns when that is the local machine.
|
|
30
|
+
|
|
29
31
|
For credential rotation, replace `MCP_AUTH_TOKEN` and the ETAPI token in the provider/Trilium, set a fresh `BRAINLLM_OAUTH_SECRET` (32+ characters) to invalidate existing OAuth tokens, and remove obsolete local `*.oauth.json` / `*.token` files. Never put recovered credentials in a repository, shared context note, Docker context, or release artifact.
|
|
30
32
|
|
|
31
33
|
## Scope
|