@hilbras/remembra 0.4.0 → 3.1.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/CHANGELOG.md +35 -0
- package/README.md +8 -0
- package/dist/http.d.ts +17 -1
- package/dist/http.js +78 -22
- package/dist/http.js.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/store.js +36 -7
- package/dist/store.js.map +1 -1
- package/dist/types.d.ts +21 -1
- package/dist/types.js +23 -1
- package/dist/types.js.map +1 -1
- package/docs/chatgpt.md +3 -1
- package/docs/clients.md +2 -0
- package/docs/security.md +89 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,41 @@
|
|
|
3
3
|
All notable changes to Remembra will be documented in this file.
|
|
4
4
|
Format follows [Keep a Changelog](https://keepachangelog.com/).
|
|
5
5
|
|
|
6
|
+
> **Versioning:** from 3.0.0 on, package versions match roadmap milestones
|
|
7
|
+
> (3.0.0 = v3). Earlier releases used independent semver: 0.1.0 = v1,
|
|
8
|
+
> 0.2.0 = v1.5, 0.3.0 = v2, 0.4.0 = v3.
|
|
9
|
+
|
|
10
|
+
## [3.1.0] — 2026-09-23
|
|
11
|
+
|
|
12
|
+
**Security hardening** in response to the deep audit (`HILBRAS-MEMORY-DEEP-AUDIT.md`).
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- **P0 directory traversal**: scopes containing `..` are rejected by validation,
|
|
16
|
+
and `fileFor()` verifies the resolved path stays under `REMEMBRA_HOME`
|
|
17
|
+
(defense in depth). Penetration test added.
|
|
18
|
+
- **Default-deny HTTP**: no API key → binds `127.0.0.1` only; non-loopback
|
|
19
|
+
`REMEMBRA_HOST` without a key refuses to start.
|
|
20
|
+
- **Timing-safe API key comparison** (`crypto.timingSafeEqual`).
|
|
21
|
+
- **Request body size limit** (10 MiB default, `REMEMBRA_MAX_BODY`) → `413`.
|
|
22
|
+
- **Atomic writes** (temp file + `rename`) — crash can no longer leave
|
|
23
|
+
half-written memory files.
|
|
24
|
+
- **ID length** 8 → 12 hex chars (2⁴⁸) + existence check on store (collision-safe).
|
|
25
|
+
- **Digest validation** on both transports (`DigestInput` Zod schema).
|
|
26
|
+
- **`Content-Length`** on all HTTP responses.
|
|
27
|
+
- `memory_list` MCP tool now exposes `includeArchived`; HTTP accepts
|
|
28
|
+
`includeArchived=true`.
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
- `docs/security.md` — trust model (incl. role prompt-injection guidance),
|
|
32
|
+
enforced protections, deployment checklist.
|
|
33
|
+
- 14 security tests (traversal pen test ×2, listen policy, body limit,
|
|
34
|
+
digest validation, atomic-write leftovers, ID uniqueness).
|
|
35
|
+
|
|
36
|
+
## [3.0.0] — 2026-09-23
|
|
37
|
+
|
|
38
|
+
**Same content as 0.4.0** — version renumbered so the package version equals the
|
|
39
|
+
roadmap milestone (v3). No behavior changes.
|
|
40
|
+
|
|
6
41
|
## [0.4.0] — 2026-09-23
|
|
7
42
|
|
|
8
43
|
### Added
|
package/README.md
CHANGED
|
@@ -96,6 +96,11 @@ Then wire a Custom GPT to the API — full walkthrough in **[docs/chatgpt.md](do
|
|
|
96
96
|
All routes except `/health` require `x-api-key` (or `Authorization: Bearer`) when
|
|
97
97
|
`REMEMBRA_API_KEY` is set.
|
|
98
98
|
|
|
99
|
+
> 🔒 **Auth is now default-deny**: without `REMEMBRA_API_KEY` the server binds
|
|
100
|
+
> to `127.0.0.1` only, and a non-loopback `REMEMBRA_HOST` without a key refuses
|
|
101
|
+
> to start. Public deployments (ChatGPT) must set a key. See
|
|
102
|
+
> [security.md](docs/security.md).
|
|
103
|
+
|
|
99
104
|
## Tools
|
|
100
105
|
|
|
101
106
|
| Tool | Purpose |
|
|
@@ -119,6 +124,7 @@ Full reference: **[docs/tools.md](docs/tools.md)**
|
|
|
119
124
|
| [ChatGPT setup](docs/chatgpt.md) | HTTP API + Custom GPT walkthrough |
|
|
120
125
|
| [AI providers](docs/providers.md) | Digest LLM + embeddings configuration |
|
|
121
126
|
| [Lifecycle](docs/lifecycle.md) | Decay, archiving, auto-delete, contradiction merging |
|
|
127
|
+
| [Security](docs/security.md) | Trust model, enforced protections, deployment checklist |
|
|
122
128
|
| [Contributing](CONTRIBUTING.md) | Dev workflow and guidelines |
|
|
123
129
|
| [Changelog](CHANGELOG.md) | Release history |
|
|
124
130
|
|
|
@@ -152,6 +158,8 @@ npm test # run tests
|
|
|
152
158
|
- **v2** — automatic session-digest extraction, embeddings behind `memory_search` ✅
|
|
153
159
|
- **v3** *(current)* — memory lifecycle (archive/decay), contradiction merging, maintenance CLI ✅
|
|
154
160
|
|
|
161
|
+
> Package versions match milestones: `3.0.0` = v3, next ships as `4.0.0`.
|
|
162
|
+
|
|
155
163
|
## License
|
|
156
164
|
|
|
157
165
|
[MIT](LICENSE) © Hilbras
|
package/dist/http.d.ts
CHANGED
|
@@ -2,9 +2,23 @@ import http from "node:http";
|
|
|
2
2
|
import { MemoryService } from "./service.js";
|
|
3
3
|
interface HttpOptions {
|
|
4
4
|
port?: number;
|
|
5
|
+
/** Bind address. Defaults: loopback when no API key, all interfaces when keyed. */
|
|
6
|
+
host?: string;
|
|
5
7
|
/** If set, all endpoints except GET /health require `x-api-key` or `Authorization: Bearer`. */
|
|
6
8
|
apiKey?: string;
|
|
9
|
+
/** Max request body bytes (default: REMEMBRA_MAX_BODY or 10 MiB). */
|
|
10
|
+
maxBodyBytes?: number;
|
|
7
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Listen policy (P1 audit: default-deny):
|
|
14
|
+
* - explicit REMEMBRA_HOST: non-loopback requires an API key (refuse otherwise)
|
|
15
|
+
* - no host + API key: all interfaces (public ChatGPT deployments)
|
|
16
|
+
* - no host + no key: loopback only (safe local default)
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveListen(host: string | undefined, hasKey: boolean): {
|
|
19
|
+
host?: string;
|
|
20
|
+
error?: string;
|
|
21
|
+
};
|
|
8
22
|
/**
|
|
9
23
|
* Minimal HTTP API over the same handlers the MCP tools use.
|
|
10
24
|
*
|
|
@@ -12,7 +26,9 @@ interface HttpOptions {
|
|
|
12
26
|
* GET /health → liveness (no auth)
|
|
13
27
|
* POST /memories → store a memory
|
|
14
28
|
* GET /memories/search → ?query=&scope=&type=&limit=
|
|
15
|
-
* GET /memories → ?scope=&type=
|
|
29
|
+
* GET /memories → ?scope=&type=&includeArchived=
|
|
30
|
+
* POST /memories/digest → LLM extraction
|
|
31
|
+
* POST /maintain → decay sweep + vector backfill
|
|
16
32
|
* DELETE /memories/:id → forget
|
|
17
33
|
*/
|
|
18
34
|
export declare function createHttpServer(service: MemoryService, opts?: HttpOptions): http.Server;
|
package/dist/http.js
CHANGED
|
@@ -1,4 +1,25 @@
|
|
|
1
1
|
import http from "node:http";
|
|
2
|
+
import { timingSafeEqual } from "node:crypto";
|
|
3
|
+
import { DigestInput } from "./types.js";
|
|
4
|
+
const DEFAULT_MAX_BODY = 10 * 1024 * 1024; // transcripts can be large — 10 MiB
|
|
5
|
+
/**
|
|
6
|
+
* Listen policy (P1 audit: default-deny):
|
|
7
|
+
* - explicit REMEMBRA_HOST: non-loopback requires an API key (refuse otherwise)
|
|
8
|
+
* - no host + API key: all interfaces (public ChatGPT deployments)
|
|
9
|
+
* - no host + no key: loopback only (safe local default)
|
|
10
|
+
*/
|
|
11
|
+
export function resolveListen(host, hasKey) {
|
|
12
|
+
if (host) {
|
|
13
|
+
const loopback = host === "localhost" || host.startsWith("127.") || host === "::1";
|
|
14
|
+
if (!loopback && !hasKey) {
|
|
15
|
+
return {
|
|
16
|
+
error: `Refusing to listen on ${host} without REMEMBRA_API_KEY. Set a key, or bind to 127.0.0.1.`,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return { host };
|
|
20
|
+
}
|
|
21
|
+
return hasKey ? {} : { host: "127.0.0.1" };
|
|
22
|
+
}
|
|
2
23
|
/**
|
|
3
24
|
* Minimal HTTP API over the same handlers the MCP tools use.
|
|
4
25
|
*
|
|
@@ -6,10 +27,16 @@ import http from "node:http";
|
|
|
6
27
|
* GET /health → liveness (no auth)
|
|
7
28
|
* POST /memories → store a memory
|
|
8
29
|
* GET /memories/search → ?query=&scope=&type=&limit=
|
|
9
|
-
* GET /memories → ?scope=&type=
|
|
30
|
+
* GET /memories → ?scope=&type=&includeArchived=
|
|
31
|
+
* POST /memories/digest → LLM extraction
|
|
32
|
+
* POST /maintain → decay sweep + vector backfill
|
|
10
33
|
* DELETE /memories/:id → forget
|
|
11
34
|
*/
|
|
12
35
|
export function createHttpServer(service, opts = {}) {
|
|
36
|
+
const maxBody = opts.maxBodyBytes ?? Number(process.env.REMEMBRA_MAX_BODY ?? DEFAULT_MAX_BODY);
|
|
37
|
+
const listenTarget = resolveListen(opts.host ?? process.env.REMEMBRA_HOST, Boolean(opts.apiKey));
|
|
38
|
+
if (listenTarget.error)
|
|
39
|
+
throw new Error(listenTarget.error);
|
|
13
40
|
const server = http.createServer(async (req, res) => {
|
|
14
41
|
try {
|
|
15
42
|
const url = new URL(req.url ?? "/", "http://localhost");
|
|
@@ -27,20 +54,13 @@ export function createHttpServer(service, opts = {}) {
|
|
|
27
54
|
}
|
|
28
55
|
// POST /memories/digest — must be checked before /memories/:id DELETE patterns
|
|
29
56
|
if (req.method === "POST" && path === "/memories/digest") {
|
|
30
|
-
const body = await readBody(req);
|
|
31
|
-
|
|
32
|
-
return send(res, 400, { error: "Body must include a non-empty `transcript` string" });
|
|
33
|
-
}
|
|
34
|
-
const result = await service.digest({
|
|
35
|
-
transcript: body.transcript,
|
|
36
|
-
scope: body.scope,
|
|
37
|
-
source: body.source,
|
|
38
|
-
});
|
|
57
|
+
const body = await readBody(req, maxBody);
|
|
58
|
+
const result = await service.digest(DigestInput.parse(body));
|
|
39
59
|
return send(res, 200, result);
|
|
40
60
|
}
|
|
41
61
|
// POST /memories
|
|
42
62
|
if (req.method === "POST" && path === "/memories") {
|
|
43
|
-
const body = await readBody(req);
|
|
63
|
+
const body = await readBody(req, maxBody);
|
|
44
64
|
const result = await service.store(body);
|
|
45
65
|
return send(res, 201, result);
|
|
46
66
|
}
|
|
@@ -59,6 +79,7 @@ export function createHttpServer(service, opts = {}) {
|
|
|
59
79
|
const result = await service.list({
|
|
60
80
|
scope: url.searchParams.get("scope") ?? undefined,
|
|
61
81
|
type: url.searchParams.get("type") ?? undefined,
|
|
82
|
+
includeArchived: url.searchParams.get("includeArchived") === "true",
|
|
62
83
|
});
|
|
63
84
|
return send(res, 200, result);
|
|
64
85
|
}
|
|
@@ -72,34 +93,69 @@ export function createHttpServer(service, opts = {}) {
|
|
|
72
93
|
}
|
|
73
94
|
catch (err) {
|
|
74
95
|
const name = err.name;
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
96
|
+
const status = name === "ZodError" || name === "BadRequestError"
|
|
97
|
+
? 400
|
|
98
|
+
: name === "PayloadTooLarge"
|
|
99
|
+
? 413
|
|
100
|
+
: 500;
|
|
101
|
+
send(res, status, { error: err instanceof Error ? err.message : String(err) });
|
|
79
102
|
}
|
|
80
103
|
});
|
|
81
104
|
const port = opts.port ?? Number(process.env.REMEMBRA_PORT ?? 8787);
|
|
82
|
-
|
|
83
|
-
|
|
105
|
+
const address = listenTarget.host;
|
|
106
|
+
server.listen(port, address, () => {
|
|
107
|
+
const where = address ?? "0.0.0.0";
|
|
108
|
+
console.error(`Remembra HTTP API listening on ${where}:${port}${opts.apiKey ? " (auth required)" : " (no auth — loopback only)"}`);
|
|
84
109
|
});
|
|
85
110
|
return server;
|
|
86
111
|
}
|
|
112
|
+
/** Constant-time API key comparison (P1 audit: timing side-channel). */
|
|
87
113
|
function authorized(req, key) {
|
|
88
114
|
const header = req.headers["x-api-key"];
|
|
89
115
|
const auth = req.headers.authorization;
|
|
90
116
|
const provided = (typeof header === "string" ? header : undefined) ??
|
|
91
117
|
(auth?.startsWith("Bearer ") ? auth.slice(7) : undefined);
|
|
92
|
-
|
|
118
|
+
if (provided === undefined)
|
|
119
|
+
return false;
|
|
120
|
+
const a = Buffer.from(provided);
|
|
121
|
+
const b = Buffer.from(key);
|
|
122
|
+
if (a.length !== b.length) {
|
|
123
|
+
// Still do one compare of equal-length buffers to blunt timing signal.
|
|
124
|
+
timingSafeEqual(b, b);
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
return timingSafeEqual(a, b);
|
|
93
128
|
}
|
|
94
129
|
function send(res, status, body) {
|
|
95
130
|
const data = JSON.stringify(body, null, 2);
|
|
96
|
-
res.writeHead(status, {
|
|
131
|
+
res.writeHead(status, {
|
|
132
|
+
"content-type": "application/json; charset=utf-8",
|
|
133
|
+
"content-length": Buffer.byteLength(data),
|
|
134
|
+
});
|
|
97
135
|
res.end(data);
|
|
98
136
|
}
|
|
99
|
-
function readBody(req) {
|
|
137
|
+
function readBody(req, maxBytes) {
|
|
100
138
|
return new Promise((resolve, reject) => {
|
|
101
|
-
|
|
102
|
-
|
|
139
|
+
const declared = Number(req.headers["content-length"] ?? 0);
|
|
140
|
+
if (Number.isFinite(declared) && declared > maxBytes) {
|
|
141
|
+
const err = new Error(`Request body too large (limit ${maxBytes} bytes)`);
|
|
142
|
+
err.name = "PayloadTooLarge";
|
|
143
|
+
reject(err);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const chunks = [];
|
|
147
|
+
let total = 0;
|
|
148
|
+
req.on("data", (c) => {
|
|
149
|
+
total += c.length;
|
|
150
|
+
if (total > maxBytes) {
|
|
151
|
+
const err = new Error(`Request body too large (limit ${maxBytes} bytes)`);
|
|
152
|
+
err.name = "PayloadTooLarge";
|
|
153
|
+
reject(err);
|
|
154
|
+
req.destroy();
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
chunks.push(c);
|
|
158
|
+
});
|
|
103
159
|
req.on("end", () => {
|
|
104
160
|
const raw = Buffer.concat(chunks).toString("utf8");
|
|
105
161
|
if (!raw)
|
package/dist/http.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { z } from "zod";
|
|
|
5
5
|
import { MemoryStore } from "./store.js";
|
|
6
6
|
import { MemoryService } from "./service.js";
|
|
7
7
|
import { createHttpServer } from "./http.js";
|
|
8
|
+
import { DigestInput } from "./types.js";
|
|
8
9
|
const store = new MemoryStore(MemoryStore.defaultRoot());
|
|
9
10
|
const service = new MemoryService(store);
|
|
10
11
|
const httpFlag = process.argv.includes("--http");
|
|
@@ -29,7 +30,7 @@ else {
|
|
|
29
30
|
await startMcp();
|
|
30
31
|
}
|
|
31
32
|
async function startMcp() {
|
|
32
|
-
const server = new McpServer({ name: "remembra", version: "
|
|
33
|
+
const server = new McpServer({ name: "remembra", version: "3.1.0" });
|
|
33
34
|
server.registerTool("memory_store", {
|
|
34
35
|
title: "Store a memory",
|
|
35
36
|
description: "Persist a fact, decision, role or history entry so it survives context window resets. " +
|
|
@@ -62,8 +63,8 @@ async function startMcp() {
|
|
|
62
63
|
scope: z.string().optional().describe("Scope for extracted memories (default: global)"),
|
|
63
64
|
source: z.string().optional().describe("Originating session/client"),
|
|
64
65
|
},
|
|
65
|
-
}, async (
|
|
66
|
-
const result = await service.digest(
|
|
66
|
+
}, async (args) => {
|
|
67
|
+
const result = await service.digest(DigestInput.parse(args));
|
|
67
68
|
const text = `Digest complete: ${result.extracted} extracted, ${result.stored.length} stored, ` +
|
|
68
69
|
`${result.merged} merged/revived, ${result.skippedDuplicates} duplicates skipped.` +
|
|
69
70
|
(result.ids.length ? `\nStored ids: ${result.ids.join(", ")}` : "");
|
|
@@ -103,6 +104,7 @@ async function startMcp() {
|
|
|
103
104
|
inputSchema: {
|
|
104
105
|
scope: z.string().optional(),
|
|
105
106
|
type: z.enum(["fact", "decision", "role", "history"]).optional(),
|
|
107
|
+
includeArchived: z.boolean().optional().describe("Include archived memories (flagged)"),
|
|
106
108
|
},
|
|
107
109
|
}, async (args) => {
|
|
108
110
|
const result = await service.list(args);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/store.js
CHANGED
|
@@ -29,12 +29,22 @@ export class MemoryStore {
|
|
|
29
29
|
? path.join(this.root, "archived", "global")
|
|
30
30
|
: path.join(this.root, "archived", "scopes", safeScope);
|
|
31
31
|
const dir = m.archivedAt ? archivedBase : base;
|
|
32
|
-
|
|
32
|
+
const file = path.resolve(path.join(dir, `${m.id}.md`));
|
|
33
|
+
// Defense in depth (P0): never touch anything outside the storage root.
|
|
34
|
+
const root = path.resolve(this.root);
|
|
35
|
+
if (!file.startsWith(root + path.sep)) {
|
|
36
|
+
throw new Error(`Invalid scope "${m.scope}": resolves outside the storage root`);
|
|
37
|
+
}
|
|
38
|
+
return file;
|
|
33
39
|
}
|
|
34
40
|
async store(input, embedding) {
|
|
35
41
|
const now = new Date().toISOString();
|
|
42
|
+
// 12 hex chars (2^48): collision-safe; existence check guards the rest (P2 audit #9).
|
|
43
|
+
let id = genId();
|
|
44
|
+
for (let i = 0; i < 10 && (await this.findFile(id)); i++)
|
|
45
|
+
id = genId();
|
|
36
46
|
const memory = {
|
|
37
|
-
id
|
|
47
|
+
id,
|
|
38
48
|
type: input.type,
|
|
39
49
|
content: input.content,
|
|
40
50
|
scope: input.scope,
|
|
@@ -47,7 +57,7 @@ export class MemoryStore {
|
|
|
47
57
|
};
|
|
48
58
|
const file = this.fileFor(memory);
|
|
49
59
|
await fs.mkdir(path.dirname(file), { recursive: true });
|
|
50
|
-
await
|
|
60
|
+
await writeFileAtomic(file, render(memory), fs);
|
|
51
61
|
return memory;
|
|
52
62
|
}
|
|
53
63
|
async forget(id) {
|
|
@@ -84,7 +94,7 @@ export class MemoryStore {
|
|
|
84
94
|
if (oldFile === newFile)
|
|
85
95
|
return null;
|
|
86
96
|
await fs.mkdir(path.dirname(newFile), { recursive: true });
|
|
87
|
-
await
|
|
97
|
+
await writeFileAtomic(newFile, render(updated), fs);
|
|
88
98
|
await fs.unlink(oldFile);
|
|
89
99
|
return updated;
|
|
90
100
|
}
|
|
@@ -98,7 +108,7 @@ export class MemoryStore {
|
|
|
98
108
|
const updated = { ...m, archivedAt: undefined, lastSeen: now, updatedAt: now };
|
|
99
109
|
const newFile = this.fileFor(updated);
|
|
100
110
|
await fs.mkdir(path.dirname(newFile), { recursive: true });
|
|
101
|
-
await
|
|
111
|
+
await writeFileAtomic(newFile, render(updated), fs);
|
|
102
112
|
await fs.unlink(oldFile);
|
|
103
113
|
return updated;
|
|
104
114
|
}
|
|
@@ -107,7 +117,7 @@ export class MemoryStore {
|
|
|
107
117
|
const updated = { ...memory, updatedAt: new Date().toISOString() };
|
|
108
118
|
const file = this.fileFor(updated);
|
|
109
119
|
await fs.mkdir(path.dirname(file), { recursive: true });
|
|
110
|
-
await
|
|
120
|
+
await writeFileAtomic(file, render(updated), fs);
|
|
111
121
|
return updated;
|
|
112
122
|
}
|
|
113
123
|
/** Record that a memory surfaced in search (decay refresh). Cheap: no-op if seen <1h ago. */
|
|
@@ -119,13 +129,32 @@ export class MemoryStore {
|
|
|
119
129
|
if (Number.isFinite(last) && Date.now() - last < 3_600_000)
|
|
120
130
|
return;
|
|
121
131
|
m.lastSeen = new Date().toISOString();
|
|
122
|
-
await
|
|
132
|
+
await writeFileAtomic(this.fileFor(m), render(m), fs);
|
|
123
133
|
}
|
|
124
134
|
async findFile(id) {
|
|
125
135
|
const files = await walk(path.join(this.root, "global"), path.join(this.root, "scopes"), path.join(this.root, "archived"));
|
|
126
136
|
return files.find((f) => path.basename(f, ".md") === id) ?? null;
|
|
127
137
|
}
|
|
128
138
|
}
|
|
139
|
+
function genId() {
|
|
140
|
+
return randomUUID().replace(/-/g, "").slice(0, 12);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Atomic write (P1 audit): write to a temp file in the same directory,
|
|
144
|
+
* then rename() over the target — POSIX-atomic, so a crash mid-write can
|
|
145
|
+
* never leave a half-written memory file.
|
|
146
|
+
*/
|
|
147
|
+
async function writeFileAtomic(file, data, fsmod) {
|
|
148
|
+
const tmp = `${file}.${genId().slice(0, 6)}.tmp`;
|
|
149
|
+
try {
|
|
150
|
+
await fsmod.writeFile(tmp, data, "utf8");
|
|
151
|
+
await fsmod.rename(tmp, file);
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
await fsmod.unlink(tmp).catch(() => { });
|
|
155
|
+
throw err;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
129
158
|
async function walk(...dirs) {
|
|
130
159
|
const out = [];
|
|
131
160
|
for (const dir of dirs) {
|
package/dist/store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,WAAW;IACO;IAA7B,YAA6B,IAAY;QAAZ,SAAI,GAAJ,IAAI,CAAQ;IAAG,CAAC;IAE7C,MAAM,CAAC,WAAW;QAChB,OAAO,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;IAC3E,CAAC;IAEO,OAAO,CAAC,CAAS;QACvB,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,mBAAmB,EAAE,GAAG,CAAC,CAAC;QAC9F,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;QAC/G,MAAM,YAAY,GAChB,CAAC,CAAC,KAAK,KAAK,QAAQ;YAClB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC;YAC5C,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;QAC5D,MAAM,GAAG,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC;QAC/C,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -25,10 +25,16 @@ export interface Memory {
|
|
|
25
25
|
/** Cached embedding vector (REMEMBRA_EMBEDDINGS≠none); serialized in frontmatter. */
|
|
26
26
|
embedding?: number[];
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Scope safety: reject `..` path segments so scope can never escape the
|
|
30
|
+
* storage root (P0 directory traversal fix). Absolute paths like
|
|
31
|
+
* `/home/user/project` are fine — path.join keeps them contained.
|
|
32
|
+
*/
|
|
33
|
+
export declare function isSafeScope(scope: string): boolean;
|
|
28
34
|
export declare const StoreInput: z.ZodObject<{
|
|
29
35
|
type: z.ZodEnum<["fact", "decision", "role", "history"]>;
|
|
30
36
|
content: z.ZodString;
|
|
31
|
-
scope: z.ZodDefault<z.ZodString>;
|
|
37
|
+
scope: z.ZodEffects<z.ZodDefault<z.ZodString>, string, string | undefined>;
|
|
32
38
|
tags: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
33
39
|
importance: z.ZodDefault<z.ZodNumber>;
|
|
34
40
|
source: z.ZodOptional<z.ZodString>;
|
|
@@ -48,6 +54,20 @@ export declare const StoreInput: z.ZodObject<{
|
|
|
48
54
|
source?: string | undefined;
|
|
49
55
|
}>;
|
|
50
56
|
export type StoreInput = z.infer<typeof StoreInput>;
|
|
57
|
+
export declare const DigestInput: z.ZodObject<{
|
|
58
|
+
transcript: z.ZodString;
|
|
59
|
+
scope: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
60
|
+
source: z.ZodOptional<z.ZodString>;
|
|
61
|
+
}, "strip", z.ZodTypeAny, {
|
|
62
|
+
transcript: string;
|
|
63
|
+
scope?: string | undefined;
|
|
64
|
+
source?: string | undefined;
|
|
65
|
+
}, {
|
|
66
|
+
transcript: string;
|
|
67
|
+
scope?: string | undefined;
|
|
68
|
+
source?: string | undefined;
|
|
69
|
+
}>;
|
|
70
|
+
export type DigestInput = z.infer<typeof DigestInput>;
|
|
51
71
|
export interface SearchQuery {
|
|
52
72
|
query?: string;
|
|
53
73
|
scope?: string;
|
package/dist/types.js
CHANGED
|
@@ -1,12 +1,34 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
/** The four memory types Remembra stores. */
|
|
3
3
|
export const MemoryType = z.enum(["fact", "decision", "role", "history"]);
|
|
4
|
+
/**
|
|
5
|
+
* Scope safety: reject `..` path segments so scope can never escape the
|
|
6
|
+
* storage root (P0 directory traversal fix). Absolute paths like
|
|
7
|
+
* `/home/user/project` are fine — path.join keeps them contained.
|
|
8
|
+
*/
|
|
9
|
+
export function isSafeScope(scope) {
|
|
10
|
+
if (scope === "global")
|
|
11
|
+
return true;
|
|
12
|
+
const normalized = scope.replace(/[^a-zA-Z0-9._/-]/g, "_");
|
|
13
|
+
return !normalized.split("/").some((seg) => seg === "..");
|
|
14
|
+
}
|
|
4
15
|
export const StoreInput = z.object({
|
|
5
16
|
type: MemoryType,
|
|
6
17
|
content: z.string().min(1),
|
|
7
|
-
scope: z
|
|
18
|
+
scope: z
|
|
19
|
+
.string()
|
|
20
|
+
.default("global")
|
|
21
|
+
.refine(isSafeScope, { message: "scope must not contain '..' path segments" }),
|
|
8
22
|
tags: z.array(z.string()).default([]),
|
|
9
23
|
importance: z.number().int().min(1).max(5).default(3),
|
|
10
24
|
source: z.string().optional(),
|
|
11
25
|
});
|
|
26
|
+
export const DigestInput = z.object({
|
|
27
|
+
transcript: z.string().min(1),
|
|
28
|
+
scope: z
|
|
29
|
+
.string()
|
|
30
|
+
.refine(isSafeScope, { message: "scope must not contain '..' path segments" })
|
|
31
|
+
.optional(),
|
|
32
|
+
source: z.string().max(500).optional(),
|
|
33
|
+
});
|
|
12
34
|
//# sourceMappingURL=types.js.map
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,6CAA6C;AAC7C,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;AA4B1E,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC;IACjC,IAAI,EAAE,UAAU;IAChB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,6CAA6C;AAC7C,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;AA4B1E;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,IAAI,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACpC,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,mBAAmB,EAAE,GAAG,CAAC,CAAC;IAC3D,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC;IACjC,IAAI,EAAE,UAAU;IAChB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1B,KAAK,EAAE,CAAC;SACL,MAAM,EAAE;SACR,OAAO,CAAC,QAAQ,CAAC;SACjB,MAAM,CAAC,WAAW,EAAE,EAAE,OAAO,EAAE,2CAA2C,EAAE,CAAC;IAChF,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IACrC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC9B,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7B,KAAK,EAAE,CAAC;SACL,MAAM,EAAE;SACR,MAAM,CAAC,WAAW,EAAE,EAAE,OAAO,EAAE,2CAA2C,EAAE,CAAC;SAC7E,QAAQ,EAAE;IACb,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;CACvC,CAAC,CAAC"}
|
package/docs/chatgpt.md
CHANGED
|
@@ -19,7 +19,9 @@ curl http://localhost:8787/health
|
|
|
19
19
|
# {"status": "ok"}
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
- `REMEMBRA_API_KEY` **
|
|
22
|
+
- `REMEMBRA_API_KEY` **mandatory for public exposure** — without it the server
|
|
23
|
+
binds to `127.0.0.1` only, and a non-loopback `REMEMBRA_HOST` without a key
|
|
24
|
+
refuses to start (default-deny, see [security.md](security.md)).
|
|
23
25
|
- The server binds to localhost by default. For ChatGPT to reach it you must expose it
|
|
24
26
|
publicly (see [§4](#4-exposing-the-server)).
|
|
25
27
|
|
package/docs/clients.md
CHANGED
|
@@ -90,6 +90,8 @@ REMEMBRA_API_KEY="your-secret" remembra --http
|
|
|
90
90
|
| `REMEMBRA_EMBEDDINGS` | `none` | Semantic search: `openai` \| `ollama` \| `none` |
|
|
91
91
|
| `REMEMBRA_ARCHIVE_AFTER_DAYS` | `90` | Unused active memory → archived |
|
|
92
92
|
| `REMEMBRA_ARCHIVE_TTL_DAYS` | `365` | Archived memory → deleted |
|
|
93
|
+
| `REMEMBRA_HOST` | *(see security.md)* | HTTP bind address (loopback without key) |
|
|
94
|
+
| `REMEMBRA_MAX_BODY` | `10485760` | Max HTTP request body bytes |
|
|
93
95
|
|
|
94
96
|
LLM/embedding key setup: see **[providers.md](providers.md)**.
|
|
95
97
|
|
package/docs/security.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Security & Trust Model
|
|
2
|
+
|
|
3
|
+
Remembra v3.1.0 hardening notes — what's protected, what's a trust decision,
|
|
4
|
+
and how to deploy safely.
|
|
5
|
+
|
|
6
|
+
## Trust model
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
┌─ Trusted ─────────────────────────────────────────────┐
|
|
10
|
+
│ You / your MCP clients (OpenCode, Claude Code, ...) │
|
|
11
|
+
│ The LLM you configure for digest/merge │
|
|
12
|
+
│ Files under REMEMBRA_HOME (local filesystem trust) │
|
|
13
|
+
└───────────────────────────────────────────────────────┘
|
|
14
|
+
┌─ Semi-trusted ────────────────────────────────────────┐
|
|
15
|
+
│ Transcripts passed to memory_digest │
|
|
16
|
+
│ Anyone holding your REMEMBRA_API_KEY │
|
|
17
|
+
└───────────────────────────────────────────────────────┘
|
|
18
|
+
┌─ Untrusted ───────────────────────────────────────────┐
|
|
19
|
+
│ Network peers (if HTTP is exposed) │
|
|
20
|
+
│ Memory content written by other users of shared AI │
|
|
21
|
+
└───────────────────────────────────────────────────────┘
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Role memories are instructions — the prompt-injection surface
|
|
25
|
+
|
|
26
|
+
`role` memories always surface (+1000) and are meant to be **followed**, not
|
|
27
|
+
merely recalled. That is the feature — and the risk:
|
|
28
|
+
|
|
29
|
+
- **Via MCP**: whoever can call `memory_store` in your session is already the
|
|
30
|
+
agent you're running. No additional boundary is crossed.
|
|
31
|
+
- **Via HTTP**: anyone with your API key can plant a global `role` that every
|
|
32
|
+
future session will receive as an instruction.
|
|
33
|
+
|
|
34
|
+
**Rules:**
|
|
35
|
+
1. Never expose HTTP without `REMEMBRA_API_KEY` (enforced — see below).
|
|
36
|
+
2. Treat the API key as root access to your assistant's standing instructions.
|
|
37
|
+
3. Audit roles periodically: `memory_list { type: "role", includeArchived: true }`.
|
|
38
|
+
4. If you ingest transcripts from other people (shared ChatGPT GPT, public bot),
|
|
39
|
+
remember the digest LLM can extract `role` items from *their* text — review
|
|
40
|
+
before trusting a multi-user deployment.
|
|
41
|
+
|
|
42
|
+
Retrieved memories should be treated as **data with provenance**, not commands
|
|
43
|
+
— but Remembra cannot enforce how the consuming model interprets them.
|
|
44
|
+
|
|
45
|
+
## Enforced protections (3.1.0)
|
|
46
|
+
|
|
47
|
+
| Protection | Mechanism |
|
|
48
|
+
|------------|-----------|
|
|
49
|
+
| **Directory traversal (P0)** | Scopes containing `..` rejected by Zod (`StoreInput`/`DigestInput`) + `fileFor()` re-verifies the resolved path stays under `REMEMBRA_HOME` (defense in depth) |
|
|
50
|
+
| **Default-deny HTTP** | Without `REMEMBRA_API_KEY`: binds `127.0.0.1` only. Non-loopback `REMEMBRA_HOST` without a key → **refuses to start** |
|
|
51
|
+
| **Timing-safe auth** | API keys compared with `crypto.timingSafeEqual` |
|
|
52
|
+
| **Body size limit** | 10 MiB default (`REMEMBRA_MAX_BODY`), `413` on excess — pre-checks `Content-Length` and enforces while streaming |
|
|
53
|
+
| **Digest validation** | `DigestInput` Zod schema on both MCP and HTTP paths |
|
|
54
|
+
| **Atomic writes** | temp file + `rename()` (POSIX-atomic) — no half-written memories after a crash |
|
|
55
|
+
| **ID collisions** | 12-hex IDs (2⁴⁸) + existence check on store |
|
|
56
|
+
| **Content-Length** | Set on every response |
|
|
57
|
+
|
|
58
|
+
## Deployment checklist
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# Local (safe default — loopback, key optional)
|
|
62
|
+
remembra --http
|
|
63
|
+
|
|
64
|
+
# Public (ChatGPT etc.) — key is mandatory
|
|
65
|
+
export REMEMBRA_API_KEY="$(openssl rand -hex 32)"
|
|
66
|
+
export REMEMBRA_HOST=0.0.0.0
|
|
67
|
+
remembra --http
|
|
68
|
+
# → put TLS in front (Caddy/nginx/cloudflared). Remembra speaks plain HTTP.
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
- [ ] `REMEMBRA_API_KEY` set with a long random value (≥32 bytes)
|
|
72
|
+
- [ ] TLS terminator in front for any non-loopback exposure
|
|
73
|
+
- [ ] `REMEMBRA_HOME` lives on a filesystem you back up (there is **no built-in
|
|
74
|
+
backup yet** — the files are the only copy)
|
|
75
|
+
- [ ] Periodic `memory_list {type: "role"}` audit
|
|
76
|
+
- [ ] LLM/embedding keys scoped to least privilege
|
|
77
|
+
|
|
78
|
+
## Known non-goals (current version)
|
|
79
|
+
|
|
80
|
+
- **No multi-tenancy** — one store per installation; scope isolates *projects*,
|
|
81
|
+
not *users*. Never share one instance between mutually untrusting users.
|
|
82
|
+
- **No encryption at rest** — files are plaintext markdown (by design: you can
|
|
83
|
+
read and edit them). Use filesystem-level encryption if needed.
|
|
84
|
+
- **No PII redaction** — what you store is what's written to disk.
|
|
85
|
+
- **No write locking / journal** — single-writer assumption; concurrent writers
|
|
86
|
+
from multiple machines are unsupported (atomic writes protect against crashes,
|
|
87
|
+
not interleaving).
|
|
88
|
+
- **No built-in backup/export** — planned; until then, back up `REMEMBRA_HOME`
|
|
89
|
+
with your normal file backup (it's plain files, `rsync`/git all work).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hilbras/remembra",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "External memory for AI assistants — remember facts, decisions, roles and history across sessions. MCP server for OpenCode, Claude Code, Cline, Kimi Code and more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|