crbro-memory 2.9.1 → 2.11.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/LICENSE +21 -21
- package/README.md +256 -456
- package/SECURITY.md +135 -8
- package/bin/crbro.mjs +555 -299
- package/dist/build-id.json +10 -0
- package/dist/cli/common.js +211 -0
- package/dist/cli/export.js +522 -0
- package/dist/cli/import.js +683 -0
- package/dist/cli/markdown.js +313 -0
- package/dist/cli/mcpclient.js +107 -0
- package/dist/cli/serve.js +453 -0
- package/dist/cli/view.js +358 -0
- package/dist/cli/viewer-page.js +378 -0
- package/dist/daemon/daemon.js +119 -11
- package/dist/daemon/endpoint.js +222 -21
- package/dist/daemon/lines.js +102 -13
- package/dist/daemon/proxy.js +480 -125
- package/dist/daemon/surface.js +116 -0
- package/dist/daemon/trace.js +73 -0
- package/dist/engine/atrest.js +132 -0
- package/dist/engine/backup.js +143 -19
- package/dist/engine/brain.js +22 -6
- package/dist/engine/codexconfig.js +476 -0
- package/dist/engine/cortex.js +762 -311
- package/dist/engine/doctor.js +676 -0
- package/dist/engine/evidence.js +171 -0
- package/dist/engine/heat.js +81 -37
- package/dist/engine/hippocampus.js +50 -36
- package/dist/engine/hookrefresh.js +296 -0
- package/dist/engine/hookverify.js +15 -3
- package/dist/engine/installer.js +1022 -0
- package/dist/engine/installio.js +279 -0
- package/dist/engine/keychain.js +735 -144
- package/dist/engine/maintenance.js +355 -87
- package/dist/engine/modinstall.js +98 -45
- package/dist/engine/prefrontal.js +126 -69
- package/dist/engine/projects.js +80 -0
- package/dist/engine/quarantine.js +204 -0
- package/dist/engine/secrets.js +41 -5
- package/dist/engine/shelf.js +34 -11
- package/dist/engine/synapses.js +183 -80
- package/dist/engine/triggers.js +141 -11
- package/dist/index.js +37 -3
- package/dist/mcp-surface.json +1 -0
- package/dist/miner/extractor.js +64 -17
- package/dist/miner/scheduler.js +144 -68
- package/dist/search/index.js +810 -176
- package/dist/search/quality.js +31 -0
- package/dist/search/semantic.js +208 -18
- package/dist/search/tokenize.js +44 -0
- package/dist/server.js +649 -343
- package/dist/sync/git.js +203 -72
- package/dist/sync/materialize.js +33 -8
- package/dist/sync/space.js +286 -101
- package/dist/sync/validate.js +204 -0
- package/dist/utils/budget.js +15 -2
- package/dist/utils/fs.js +40 -6
- package/dist/utils/ids.js +128 -14
- package/dist/utils/lock.js +219 -52
- package/hooks/crbro-codex.mjs +284 -0
- package/hooks/crbro-guard.mjs +281 -173
- package/hooks/crbro-hooks.shipped.json +20 -0
- package/hooks/crbro-lifecycle.mjs +16 -4
- package/hooks/crbro-subagent.mjs +180 -180
- package/package.json +12 -8
- package/dist/daemon/daemon.d.ts +0 -25
- package/dist/daemon/daemon.d.ts.map +0 -1
- package/dist/daemon/daemon.js.map +0 -1
- package/dist/daemon/endpoint.d.ts +0 -66
- package/dist/daemon/endpoint.d.ts.map +0 -1
- package/dist/daemon/endpoint.js.map +0 -1
- package/dist/daemon/lines.d.ts +0 -55
- package/dist/daemon/lines.d.ts.map +0 -1
- package/dist/daemon/lines.js.map +0 -1
- package/dist/daemon/proxy.d.ts +0 -55
- package/dist/daemon/proxy.d.ts.map +0 -1
- package/dist/daemon/proxy.js.map +0 -1
- package/dist/engine/backup.d.ts +0 -68
- package/dist/engine/backup.d.ts.map +0 -1
- package/dist/engine/backup.js.map +0 -1
- package/dist/engine/brain.d.ts +0 -79
- package/dist/engine/brain.d.ts.map +0 -1
- package/dist/engine/brain.js.map +0 -1
- package/dist/engine/cortex.d.ts +0 -670
- package/dist/engine/cortex.d.ts.map +0 -1
- package/dist/engine/cortex.js.map +0 -1
- package/dist/engine/dates.d.ts +0 -28
- package/dist/engine/dates.d.ts.map +0 -1
- package/dist/engine/dates.js.map +0 -1
- package/dist/engine/heat.d.ts +0 -28
- package/dist/engine/heat.d.ts.map +0 -1
- package/dist/engine/heat.js.map +0 -1
- package/dist/engine/hippocampus.d.ts +0 -60
- package/dist/engine/hippocampus.d.ts.map +0 -1
- package/dist/engine/hippocampus.js.map +0 -1
- package/dist/engine/hookverify.d.ts +0 -42
- package/dist/engine/hookverify.d.ts.map +0 -1
- package/dist/engine/hookverify.js.map +0 -1
- package/dist/engine/keychain.d.ts +0 -32
- package/dist/engine/keychain.d.ts.map +0 -1
- package/dist/engine/keychain.js.map +0 -1
- package/dist/engine/maintenance.d.ts +0 -218
- package/dist/engine/maintenance.d.ts.map +0 -1
- package/dist/engine/maintenance.js.map +0 -1
- package/dist/engine/modinstall.d.ts +0 -215
- package/dist/engine/modinstall.d.ts.map +0 -1
- package/dist/engine/modinstall.js.map +0 -1
- package/dist/engine/postmortem.d.ts +0 -81
- package/dist/engine/postmortem.d.ts.map +0 -1
- package/dist/engine/postmortem.js.map +0 -1
- package/dist/engine/prefrontal.d.ts +0 -61
- package/dist/engine/prefrontal.d.ts.map +0 -1
- package/dist/engine/prefrontal.js.map +0 -1
- package/dist/engine/secrets.d.ts +0 -30
- package/dist/engine/secrets.d.ts.map +0 -1
- package/dist/engine/secrets.js.map +0 -1
- package/dist/engine/shelf.d.ts +0 -156
- package/dist/engine/shelf.d.ts.map +0 -1
- package/dist/engine/shelf.js.map +0 -1
- package/dist/engine/source.d.ts +0 -6
- package/dist/engine/source.d.ts.map +0 -1
- package/dist/engine/source.js.map +0 -1
- package/dist/engine/synapses.d.ts +0 -82
- package/dist/engine/synapses.d.ts.map +0 -1
- package/dist/engine/synapses.js.map +0 -1
- package/dist/engine/triggers.d.ts +0 -53
- package/dist/engine/triggers.d.ts.map +0 -1
- package/dist/engine/triggers.js.map +0 -1
- package/dist/engine/usage.d.ts +0 -61
- package/dist/engine/usage.d.ts.map +0 -1
- package/dist/engine/usage.js.map +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/miner/extractor.d.ts +0 -12
- package/dist/miner/extractor.d.ts.map +0 -1
- package/dist/miner/extractor.js.map +0 -1
- package/dist/miner/index.d.ts +0 -61
- package/dist/miner/index.d.ts.map +0 -1
- package/dist/miner/index.js.map +0 -1
- package/dist/miner/scheduler.d.ts +0 -30
- package/dist/miner/scheduler.d.ts.map +0 -1
- package/dist/miner/scheduler.js.map +0 -1
- package/dist/miner/types.d.ts +0 -11
- package/dist/miner/types.d.ts.map +0 -1
- package/dist/miner/types.js.map +0 -1
- package/dist/search/index.d.ts +0 -323
- package/dist/search/index.d.ts.map +0 -1
- package/dist/search/index.js.map +0 -1
- package/dist/search/semantic.d.ts +0 -80
- package/dist/search/semantic.d.ts.map +0 -1
- package/dist/search/semantic.js.map +0 -1
- package/dist/search/synonyms.d.ts +0 -2
- package/dist/search/synonyms.d.ts.map +0 -1
- package/dist/search/synonyms.js.map +0 -1
- package/dist/search/tokenize.d.ts +0 -39
- package/dist/search/tokenize.d.ts.map +0 -1
- package/dist/search/tokenize.js.map +0 -1
- package/dist/server.d.ts +0 -41
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/sync/git.d.ts +0 -36
- package/dist/sync/git.d.ts.map +0 -1
- package/dist/sync/git.js.map +0 -1
- package/dist/sync/materialize.d.ts +0 -35
- package/dist/sync/materialize.d.ts.map +0 -1
- package/dist/sync/materialize.js.map +0 -1
- package/dist/sync/ops.d.ts +0 -142
- package/dist/sync/ops.d.ts.map +0 -1
- package/dist/sync/ops.js.map +0 -1
- package/dist/sync/space.d.ts +0 -116
- package/dist/sync/space.d.ts.map +0 -1
- package/dist/sync/space.js.map +0 -1
- package/dist/types/index.d.ts +0 -346
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js.map +0 -1
- package/dist/utils/budget.d.ts +0 -44
- package/dist/utils/budget.d.ts.map +0 -1
- package/dist/utils/budget.js.map +0 -1
- package/dist/utils/fs.d.ts +0 -59
- package/dist/utils/fs.d.ts.map +0 -1
- package/dist/utils/fs.js.map +0 -1
- package/dist/utils/hash.d.ts +0 -20
- package/dist/utils/hash.d.ts.map +0 -1
- package/dist/utils/hash.js.map +0 -1
- package/dist/utils/ids.d.ts +0 -47
- package/dist/utils/ids.d.ts.map +0 -1
- package/dist/utils/ids.js.map +0 -1
- package/dist/utils/lock.d.ts +0 -11
- package/dist/utils/lock.d.ts.map +0 -1
- package/dist/utils/lock.js.map +0 -1
- package/dist/utils/transcripts.d.ts +0 -63
- package/dist/utils/transcripts.d.ts.map +0 -1
- package/dist/utils/transcripts.js.map +0 -1
- package/dist/version.d.ts +0 -26
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js.map +0 -1
package/SECURITY.md
CHANGED
|
@@ -16,7 +16,8 @@ like friction, and nobody trusts one for more than it does.
|
|
|
16
16
|
| | Claude Code session logs, read by `crbro usage`, `crbro postmortem` and the lifecycle hook | `~/.claude/projects/` |
|
|
17
17
|
| | Compaction checkpoints written by the lifecycle hook (requests, task list, folder, remote) | `<brain>/checkpoints/`, seven days |
|
|
18
18
|
| **Code CRBRO runs in Claude Code without a command** | The open-items mod, installed and updated by `crbro_boot` | `~/.claude/crbro-mods/`, one entry in `env.CLAUDE_CODE_PLUGIN_DIRS` of `~/.claude/settings.json` |
|
|
19
|
-
| **Communication out** |
|
|
19
|
+
| **Communication out** | MCP tool results, including explicit secret reads | Your AI client and, depending on it, its model provider |
|
|
20
|
+
| | Team-space sync | A git remote you configure |
|
|
20
21
|
| | The optional semantic model | Downloaded once from Hugging Face by `semantic install` |
|
|
21
22
|
|
|
22
23
|
There is no account, no server of ours and no telemetry. Nothing is sent to
|
|
@@ -41,10 +42,20 @@ applies the same filter to everything it prints. Text is always redacted whole
|
|
|
41
42
|
and cut to length afterwards: a cut first could leave half a token the filter
|
|
42
43
|
no longer recognises.
|
|
43
44
|
|
|
45
|
+
Since 2.11 it also covers the decision rationale and the fact aliases
|
|
46
|
+
(`keywords`) of `crbro_learn`, and the notes of `crbro_revise`; `crbro_audit`
|
|
47
|
+
counts a credential found in any of them with its entry. A private key is
|
|
48
|
+
redacted whole — through its `END` line, or every base64 line after the
|
|
49
|
+
header when there is none — not just its header. Labels written without a
|
|
50
|
+
separator (`PASS value`, `la contraseña value (…`) are caught when the value
|
|
51
|
+
mixes character classes; plain prose after the same words is not.
|
|
52
|
+
|
|
44
53
|
It is tuned for precision, not recall: it matches shapes that are credentials
|
|
45
54
|
and essentially nothing else, because a false positive silently corrupts real
|
|
46
55
|
knowledge. The consequence is that a credential in a shape it does not know
|
|
47
|
-
passes. Treat it as a net, not as a guarantee.
|
|
56
|
+
passes. Treat it as a net, not as a guarantee. `npm run bench` measures it:
|
|
57
|
+
every synthetic value must be gone from the redacted text (not merely the text
|
|
58
|
+
changed), and the release gate allows no false positive on its innocent set.
|
|
48
59
|
|
|
49
60
|
Sharing is stricter than storing: a neuron that contains a credential is
|
|
50
61
|
**refused** for sharing, not redacted, because sending a teammate a mangled
|
|
@@ -65,13 +76,89 @@ it, and it goes back into a config file in plain text. `crbro_secret` and
|
|
|
65
76
|
- **Linux** — the Secret Service, through `secret-tool(1)`.
|
|
66
77
|
|
|
67
78
|
CRBRO invents no cryptography. The store lives outside the brain on purpose,
|
|
68
|
-
so
|
|
69
|
-
|
|
70
|
-
|
|
79
|
+
so normal memory sync, share and backups do not include that store. Keep custom
|
|
80
|
+
`CRBRO_KEYS_DIR` locations outside the brain, repositories and synced folders.
|
|
81
|
+
Never add the encrypted store to Git or a release package. The CLI reads the
|
|
82
|
+
value from stdin, never from its arguments, so it stays out of shell history.
|
|
83
|
+
How it then reaches the store depends on the platform: on Windows and Linux it
|
|
84
|
+
goes to PowerShell and `secret-tool` through stdin; on **macOS** `security(1)`
|
|
85
|
+
has no stdin form, so the value (base64-encoded, which is not encryption) is
|
|
86
|
+
on that command's argument list for the few milliseconds the call lasts, where
|
|
87
|
+
another process of the same machine could read it with `ps`.
|
|
71
88
|
|
|
72
89
|
What it does not do: protect against code running as your own user. DPAPI,
|
|
73
90
|
the login keychain and the Secret Service all open for the logged-in account.
|
|
74
91
|
|
|
92
|
+
`crbro_secret action=get` deliberately returns the value to the MCP client.
|
|
93
|
+
Local encryption does not stop that client from putting it into model context
|
|
94
|
+
or conversation logs. Prefer a credential-aware CLI that reads it internally
|
|
95
|
+
and uses it only to authenticate to the intended service; never print it for
|
|
96
|
+
convenience. There is no promise that a secret used for authentication never
|
|
97
|
+
leaves the computer.
|
|
98
|
+
|
|
99
|
+
**The environment is not a keychain (2.11).** Until 2.10 `get` returned any
|
|
100
|
+
variable of the server's environment with the requested name — `USERNAME`,
|
|
101
|
+
`ANTHROPIC_API_KEY`, whatever the client was started with — before looking at
|
|
102
|
+
the store. Now an environment variable counts only when it opts in: a variable
|
|
103
|
+
named `CRBRO_SECRET_<NAME>`, or a plain `<NAME>` that `CRBRO_SECRET_ENV`
|
|
104
|
+
(a comma-separated list) names. That keeps CI and one-off overrides working.
|
|
105
|
+
The answer says `source: env` or `source: keychain`. Every action checks the
|
|
106
|
+
name (`SCREAMING_SNAKE_CASE`).
|
|
107
|
+
|
|
108
|
+
**Destructive writes ask first (2.11).** Through MCP, `set` on a name that
|
|
109
|
+
already holds a different value and `remove` change nothing on the first call:
|
|
110
|
+
they return a `confirm_token` to show the user, and only a second call with
|
|
111
|
+
that token writes. The token is an HMAC under a key that never leaves the
|
|
112
|
+
server process, bound to the name, the state of the store and (for `set`) the
|
|
113
|
+
new value, so a stale token, another value or a restarted server is refused.
|
|
114
|
+
The CLI asks for `--yes` on `remove` as before; a person typing at a terminal
|
|
115
|
+
is the confirmation for `set`.
|
|
116
|
+
|
|
117
|
+
**A missing store is not an empty one (2.11).** On Windows a plain marker
|
|
118
|
+
(`keys.dpapi.marker`, dates only) is written next to the store on every write,
|
|
119
|
+
and every successful write keeps a byte copy of the sealed file in
|
|
120
|
+
`sealed-copies/` (the last `CRBRO_KEYS_COPIES`, default 5; copies are never
|
|
121
|
+
opened except to validate a restore, and they open for the same Windows
|
|
122
|
+
account only). If `keys.dpapi` is missing while the marker, a sealed copy or a
|
|
123
|
+
leftover `*.tmp` says it existed, every operation fails with recovery steps
|
|
124
|
+
instead of answering "no credentials", and `set` refuses to create a new store
|
|
125
|
+
over it unless `--force` is given. `npx crbro-memory secret copies` lists the
|
|
126
|
+
copies and leftovers; `npx crbro-memory secret restore <id> --yes` validates
|
|
127
|
+
one with DPAPI, keeps the current store as a copy and puts it back. A removal
|
|
128
|
+
or an overwrite can therefore be undone until five more writes rotate the copy
|
|
129
|
+
out. Stale temporary files of this release are swept after an hour; the fixed
|
|
130
|
+
`keys.dpapi.tmp` of releases up to 2.9 is never touched, because it may be the
|
|
131
|
+
only copy left. macOS and Linux keep no copies: their stores are the
|
|
132
|
+
operating system's.
|
|
133
|
+
|
|
134
|
+
On macOS and Linux the names live in an index item of their own. Only "not
|
|
135
|
+
found" reads as an empty index; a locked keychain, a denied prompt or an
|
|
136
|
+
unreadable index is an error, and nothing is written. On Linux the index
|
|
137
|
+
replaced `secret-tool search --all`, which printed every value just to list
|
|
138
|
+
names (a store from 2.10 is migrated once, keeping names and descriptions).
|
|
139
|
+
|
|
140
|
+
On Windows, read-modify-write operations are serialized across processes and
|
|
141
|
+
ciphertext changes are checked before serving cached values; the decrypted
|
|
142
|
+
cache is dropped by a timer after 30 seconds. PowerShell is started from its
|
|
143
|
+
absolute System32 path, not looked up from the client's folder. The server
|
|
144
|
+
runs every keychain operation asynchronously, so a slow PowerShell start no
|
|
145
|
+
longer stalls the other conversations of a shared daemon; the store probe is
|
|
146
|
+
remembered for five minutes. Lock timeouts fail closed and say which process
|
|
147
|
+
holds the lock and for how long. Async lock holders refresh a heartbeat, so a
|
|
148
|
+
lock whose owner died is reclaimed even when Windows has reused its PID. A
|
|
149
|
+
crashed short-lived `.lock.guard` is not stolen while waiting; maintenance
|
|
150
|
+
removes one only when it is more than ten minutes old. Stop all writers before
|
|
151
|
+
deleting locks by hand.
|
|
152
|
+
|
|
153
|
+
### Project and evidence boundaries (2.10)
|
|
154
|
+
|
|
155
|
+
Project scope is a retrieval/task-organization filter, not authorization or
|
|
156
|
+
tenant isolation. A caller with this brain's MCP access can request `scope=all`.
|
|
157
|
+
References name a file, URL or person but are never fetched or executed by the
|
|
158
|
+
memory server. `verification.method=source` records the caller's claim to have
|
|
159
|
+
checked that reference, not an independently audited or signed result. A strong
|
|
160
|
+
search match, a repeated statement, and a true statement are different things.
|
|
161
|
+
|
|
75
162
|
### Quarantine before forgetting
|
|
76
163
|
|
|
77
164
|
`crbro_forget` (and every merge, split and compaction that removes a neuron)
|
|
@@ -117,9 +204,10 @@ What it does not prove:
|
|
|
117
204
|
teammate's or the miner's line cannot be rewritten as your own lesson
|
|
118
205
|
through `promotion_candidates`.
|
|
119
206
|
|
|
120
|
-
Provenance labels; it does not sanitise.
|
|
121
|
-
|
|
122
|
-
|
|
207
|
+
Provenance labels; it does not sanitise instructions. Since 2.11 content
|
|
208
|
+
arriving through a shared space passes through the secret filter and size and
|
|
209
|
+
shape checks on the way in (see below), but nothing filters what it tells the
|
|
210
|
+
model to do. Treat any line with an `origin` as data, never as an order.
|
|
123
211
|
|
|
124
212
|
### Team sync over git
|
|
125
213
|
|
|
@@ -132,6 +220,45 @@ a changed one afterwards. Incoming logs written by a newer, unknown format are
|
|
|
132
220
|
skipped rather than half-understood, and an unreadable line is skipped without
|
|
133
221
|
costing the rest.
|
|
134
222
|
|
|
223
|
+
What 2.11 checks, because each of these was accepted as it came:
|
|
224
|
+
|
|
225
|
+
- **Space names** are slugs (letters, digits, `-`, `_`; at most 64) and must
|
|
226
|
+
resolve to a folder directly inside `shared/`. A name like `..` used to make
|
|
227
|
+
`create` run `git add -A` and push the whole brain to the given remote.
|
|
228
|
+
- **Remotes** that start with `-`, contain control characters or carry
|
|
229
|
+
credentials (`https://user:password@…`, `https://token@…`) are refused before
|
|
230
|
+
git runs, so a credential never lands in `.git/config`; use SSH or a
|
|
231
|
+
credential helper. Branches follow git's ref-name rules, including the one
|
|
232
|
+
read from a cloned `space.json`. Positional arguments follow `--`, and git's
|
|
233
|
+
error output passes through the secret filter before it is shown.
|
|
234
|
+
- **No adoption.** A sync merges a folder of the space only into a neuron you
|
|
235
|
+
already share in that space, or into one this machine does not have. A
|
|
236
|
+
folder with the id of a private neuron of yours is left alone and reported
|
|
237
|
+
(`left_alone`): a teammate can no longer turn your neuron into a shared one,
|
|
238
|
+
which would have sent everything you learn about it to the team. Share it
|
|
239
|
+
with `crbro_share` to merge it.
|
|
240
|
+
- **Incoming notes** must name the neuron of their folder (a note that names
|
|
241
|
+
another one is dropped, so it cannot steer where a neuron is written); the
|
|
242
|
+
author label is cut to the charset and length of a local identity; ids,
|
|
243
|
+
kinds and confidences are checked; text is capped and redacted; logs over
|
|
244
|
+
32 MB are skipped.
|
|
245
|
+
- **Sharing checks every field that travels**: rationales, aliases, revision
|
|
246
|
+
notes, the neuron's name, summary and tags, not only the entry texts. A
|
|
247
|
+
credential in any of them blocks the share.
|
|
248
|
+
- On Windows git is started from its absolute path on `PATH`, never looked up
|
|
249
|
+
in the space folder, whose content comes from teammates. The server runs git
|
|
250
|
+
asynchronously, so a slow push no longer stalls the shared daemon.
|
|
251
|
+
|
|
252
|
+
### The miner's schedule
|
|
253
|
+
|
|
254
|
+
`setup-miner` and `remove-miner` run every command as an argument array,
|
|
255
|
+
never through a shell. The crontab is read with `crontab -l`, your own lines
|
|
256
|
+
are kept byte for byte, the previous crontab is saved next to the brain as
|
|
257
|
+
`crontab.before-crbro-*.txt`, and the new one is written through stdin.
|
|
258
|
+
Until 2.10 it went through `echo "…" | crontab -`, which expanded `$(…)` and
|
|
259
|
+
backticks in your existing cron lines. On Windows the task runs the absolute
|
|
260
|
+
`npx.cmd`, and the registered command is checked after creation.
|
|
261
|
+
|
|
135
262
|
### Hooks never block
|
|
136
263
|
|
|
137
264
|
The guard (`PreToolUse`), the subagent hook (`SubagentStart`) and the
|