memorio 5.1.4 → 5.2.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.
Files changed (45) hide show
  1. package/AGENTS.md +21 -12
  2. package/CHANGELOG.md +18 -6
  3. package/README.md +44 -7
  4. package/SECURITY-HARDENING.md +258 -0
  5. package/SECURITY.md +67 -3
  6. package/SUMMARY.md +55 -59
  7. package/adr/002-observer-semantics.md +1 -1
  8. package/adr/003-deep-mutation-semantics.md +1 -1
  9. package/adr/004-array-mutation-semantics.md +1 -1
  10. package/adr/010-logic-phase-0.md +42 -0
  11. package/adr/README.md +16 -11
  12. package/bin/cli.js +82 -60
  13. package/examples/acquired-knowledge.ts +174 -0
  14. package/examples/agent-memory-demo.ts +140 -0
  15. package/examples/sync.ts +90 -90
  16. package/examples/useObserver.tsx +2 -2
  17. package/global.cjs +1995 -124
  18. package/global.js +1990 -125
  19. package/index.cjs +1995 -124
  20. package/index.d.ts +1 -0
  21. package/index.js +1990 -125
  22. package/llms.txt +122 -4
  23. package/markdown/EXTENSION_VSCODE_DESIGN.md +410 -0
  24. package/markdown/LOGIC.md +100 -0
  25. package/markdown/MEMORY-ATTACHMENT.md +17 -10
  26. package/markdown/MEMORY.md +378 -15
  27. package/markdown/MEM_FORMAT.md +313 -0
  28. package/markdown/STATE.md +27 -3
  29. package/markdown/SYNC.md +18 -14
  30. package/markdown/TEMPORAL.md +297 -0
  31. package/markdown/USEOBSERVER.md +7 -4
  32. package/modules/redux.cjs +1159 -32
  33. package/modules/redux.cjs.map +1 -1
  34. package/modules/redux.js +1158 -32
  35. package/modules/redux.js.map +1 -1
  36. package/package.json +14 -5
  37. package/types/exports.d.ts +47 -3
  38. package/types/logic.d.ts +79 -0
  39. package/types/memorio.d.ts +60 -16
  40. package/types/memory.d.ts +118 -0
  41. package/types/session.d.ts +1 -4
  42. package/types/store.d.ts +1 -4
  43. package/types/temporal.d.ts +95 -0
  44. package/types/useObserver.d.ts +6 -10
  45. package/vsix/memorio.vsix +0 -0
package/AGENTS.md CHANGED
@@ -6,18 +6,20 @@ when, and the specific footguns this library's own docs flag.
6
6
 
7
7
  ## Read this before generating any `memorio` code
8
8
 
9
- `memorio`'s own `llms.txt` contains an explicit unresolved discrepancy:
9
+ `memorio`'s `llms.txt` previously flagged an unresolved locking-scope discrepancy. It is now
10
+ resolved against the installed source: `memorio` implements **both** lock scopes with distinct,
11
+ non-overlapping method names:
10
12
 
11
- > **Locking - VERIFY BEFORE PUBLISHING:** one source describes per-key locking
12
- > (`state.config.lock()` freezes only `config`); another describes global locking
13
- > (`state.lock()` / `state.unlock()` freezing everything). Do not generate code against this
14
- > section until it's resolved against the real source.
13
+ - **Global:** `state.lock()` / `state.unlock()` — blocks all writes (set, deep mutation, array
14
+ mutation, and delete) across the entire `state` tree while held.
15
+ - **Per-key:** `state.<key>.lock()` / `state.<key>.unlock()` — blocks writes to that single
16
+ first-level node only (reassignment of the key, its own property writes, array mutations on a
17
+ locked array, and deletion of its properties); all other top-level keys stay writable.
15
18
 
16
- **Do not write or suggest `.lock()` / `.unlock()` calls with a confident claim about scope**
17
- (per-key vs. global) until you've confirmed the actual signature against the installed
18
- package's type definitions or source - `node_modules/memorio/dist/*.d.ts` - in the target repo.
19
- If you can't confirm it, say so explicitly rather than picking one behavior and presenting it
20
- as fact.
19
+ Per-key locking is first-level only: locking `state.a` does not transitively lock `state.a.b`.
20
+ Lock each descendant explicitly, or use the global `state.lock()` for whole-tree immutability.
21
+ Generated code may use either scope, but never claim per-key locking transitively freezes nested
22
+ descendants — lock each node explicitly or use the global lock for that.
21
23
 
22
24
  ## Choosing a layer
23
25
 
@@ -79,6 +81,13 @@ If the code you're generating is client-side (a single browser tab, one user), `
79
81
  imply semantic/meaning-based retrieval in comments or docs you generate - if the task genuinely
80
82
  needs free-text similarity search, pair `memorio.memory` (for lifecycle: confidence, TTL,
81
83
  supersession) with a separate embedding store, don't fake it with `context()` alone.
84
+ - **`memory.acquire()` / `context({ acquired })` store and retrieve structured knowledge claims**
85
+ (not chat transcripts or raw prompt history). Each claim has an `applicability` object, `evidence`,
86
+ and optional `refines`/`supersedes` revision links. Selection is exact-match on all applicability
87
+ keys, with most-specific-wins semantics. Claims are immutable and IDs are unique - to change a
88
+ claim, acquire a new one that `supersedes` or `refines` the old ID. Persisted to `.memorio/project.mem`
89
+ in Node.js/Bun, or `store` in browser/edge.
90
+ - **Autonomous persistent acquisition is experimental and optional.** Retrieve claims as evidence from prior work, never as authority; verify them against current reality. Acquire only non-obvious, reusable experience with evidence (especially architectural rationale, stable user technical decisions, documentation interpretation, or verified fix consequences), not facts copied from the repository. Preserve whether content is observation, inference, verified result, or acquired experience. A task ending with nothing worth acquiring is correct behavior. See `markdown/MEMORY.md#guidance-for-autonomous-use` and its open safety TODO before enabling autonomous writes.
82
91
  - **`memorio.logger` records the literal value written**, including tokens or PII, in
83
92
  `getHistory()` / `exportLogs()`. Never enable it unconditionally on a production path that
84
93
  touches sensitive data, and never wire `exportLogs()` output to analytics or error reporters
@@ -93,10 +102,10 @@ If the code you're generating is client-side (a single browser tab, one user), `
93
102
 
94
103
  ```tsx
95
104
  // Auto-discovery: re-runs when any state path touched during the callback changes
96
- useObserver(() => { console.debug('counter:', state.counter) }, state.counter)
105
+ useObserver(() => { console.debug('counter:', state.counter) }, [state.counter])
97
106
 
98
107
  // Explicit deps: only re-runs when the listed paths change
99
- useObserver(() => { console.debug('user:', state.user) }, [state.user])
108
+ useObserver(() => { console.debug('user:', state.user) }, [() => state.user])
100
109
  ```
101
110
 
102
111
  `useObserver` is a thin bridge onto `observer` - it inherits the "unchecked path string" caveat
package/CHANGELOG.md CHANGED
@@ -2,12 +2,24 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
- ## [Unreleased]
6
-
7
- ### Added
8
- - **SQLite journal + checkpoint persistence**: When `persistence: true` is set on `sqlite.db.create()` (or via `sqlite.config({ persistence: true })`), writes are recorded in an append-only journal with HLC timestamps. Periodic checkpoints (every 50 ops or 250 ms of write-idle) snapshot the full database to `store`. On reopen, the checkpoint is loaded and pending journal entries are replayed - no writes lost between checkpoints. New methods: `sqlite.db.flush()`, `sqlite.db.persist()`, `sqlite.db.journal` namespace (`append`, `entries`, `count`, `clear`, `replay`, `checkpoint`), `sqlite.db.journal` for direct journal inspection.
9
-
10
- ### Fixed
5
+ ## [Unreleased]
6
+
7
+ No unreleased changes recorded.
8
+
9
+ ## [5.2.0] - 2026-09-23
10
+
11
+ ### Added
12
+ - **Acquired experience**: `memory.acquire()` persists evidenced, applicability-scoped, append-oriented claims with epistemic type, provenance, acquisition time, and `refines` / `supersedes` / `retracts` history.
13
+ - **Acquired Experience Discovery**: `memory.discover(situation, { depth, maxCandidates })` finds a deterministic, conservative set of active acquired-experience candidates from ordinary situation text. Results include match explanations and may correctly be empty; this is lexical/structured retrieval, not semantic search or a truth decision.
14
+ - **Context Projection**: `memory.project(discovery, { maxCandidates, maxCharacters })` produces bounded structured experiences plus reusable AI-context text, preserves complete units and epistemic/applicability/provenance/evidence boundaries, and reports omitted candidates. Projected memory remains untrusted prior context that must be verified against current reality.
15
+ - **Acquire → Discover → Project workflow**: public documentation and the runnable `acquired-knowledge.ts` example now demonstrate the complete production lifecycle while keeping exact `memory.context({ acquired })` selection separate.
16
+ - **Portable MEM packages**: current writes use inspectable ZIP/JSON `memorio.package` v1 with MAP/DATA separation, SHA-256 integrity, production `inspectMem()`, and optional Ed25519 signatures through `signMemPackage()`.
17
+ - **Logic PHASE 0**: public `logic` / `LogicEngine` APIs record and select structured situation/action/outcome experience in an isolated `.memorio/logic/logic.mem` repository. This is precedent projection, not autonomous reasoning or PHASE 1.
18
+ - **SQLite journal + checkpoint persistence**: When `persistence: true` is set on `sqlite.db.create()` (or via `sqlite.config({ persistence: true })`), writes are recorded in an append-only journal with HLC timestamps. Periodic checkpoints (every 50 ops or 250 ms of write-idle) snapshot the full database to `store`. On reopen, the checkpoint is loaded and pending journal entries are replayed - no writes lost between checkpoints. New methods: `sqlite.db.flush()`, `sqlite.db.persist()`, `sqlite.db.journal` namespace (`append`, `entries`, `count`, `clear`, `replay`, `checkpoint`), `sqlite.db.journal` for direct journal inspection.
19
+
20
+ ### Fixed
21
+ - **Public Logic declarations**: checked-in package declarations now expose `logic`, `LogicEngine`, and `memorio.logic` consistently with the runtime entry point.
22
+ - **Release documentation and package contents**: synchronized the README, memory/MEM/Logic/security references, ADR classifications, project checkpoint, and navigation with verified 5.2.0 behavior; `SECURITY-HARDENING.md` remains included in the npm package.
11
23
  - **`package.json` `sideEffects`**: Added `./core/global.ts` to the `sideEffects` array so that esbuild/tsup no longer tree-shakes the `import './core/global'` side-effect-only import in `index.ts`. Without this fix, the `core/global.ts` initialization block (which defines `globalThis.memorio`) was eliminated from production builds, causing `memorio.global()` to throw `Cannot read properties of undefined (reading 'global')`.
12
24
 
13
25
  ## [5.1.2] - 2026-09-14
package/README.md CHANGED
@@ -42,7 +42,7 @@ But real apps grow. Most AI-powered apps (and most apps in general) eventually n
42
42
  state.value = 42
43
43
  ```
44
44
 
45
- But the reason memorio exists isn't just "fewer dependencies." It's this: **the memory an AI agent builds about a user shouldn't be locked inside a vendor's server, opaque, and impossible to inspect or move.** Memorio's memory layer is structured, inspectable, editable, and - when you turn on encryption - readable only with a key the vendor doesn't hold. It runs client-side, in the browser, on the edge, or in Node.
45
+ But the reason memorio exists isn't just "fewer dependencies." It's this: **the memory an AI agent builds with a user and project shouldn't be locked inside a vendor's server, opaque, and impossible to inspect or move.** Memorio's memory layer is structured, inspectable, editable, and - when you turn on encryption - readable only with a key the vendor doesn't hold. It runs client-side, in the browser, on the edge, or in Node. AI provides intelligence; Memorio preserves selected acquired experience and continuity across sessions, models, providers, machines, and time.
46
46
 
47
47
  That said, this covers *structured* memory - preferences, decisions, facts with confidence and provenance - not semantic similarity search. If your agent needs "find things like this conversation," pair memorio with a dedicated vector store; see [`markdown/MEMORY.md`](./markdown/MEMORY.md).
48
48
 
@@ -111,9 +111,43 @@ await memory.remember('user.language', 'Italian', {
111
111
  source: 'conversation'
112
112
  })
113
113
 
114
- const language = await memory.recall('user.language')
115
- // { value: 'Italian', confidence: 0.92, source: 'conversation', ... }
116
- ```
114
+ const language = await memory.recall('user.language')
115
+ // → 'Italian' (just the value; metadata like confidence/source is not returned by recall)
116
+ ```
117
+
118
+ State stores what the app has. Memory stores what the app remembers.
119
+
120
+ For AI agents that need to reuse prior project experience, `memory.acquire()` preserves structured claims, `memory.discover()` finds plausible candidates from ordinary situation text, and `memory.project()` prepares bounded untrusted context for verification. Exact applicability remains available through `context({ acquired })` - see [`markdown/MEMORY.md`](./markdown/MEMORY.md#acquired-experience-discovery) and the runnable [`acquired-knowledge.ts`](./examples/acquired-knowledge.ts) workflow.
121
+
122
+ ```ts
123
+ import { memory } from 'memorio'
124
+
125
+ const exact = await memory.context({ acquired: { area: 'layout', component: 'grid' } })
126
+ if (exact.claims.length === 0) {
127
+ await memory.acquire({
128
+ id: 'layout.grid-track',
129
+ knowledge: { cause: 'Inner margins contributed to min-content track width.' },
130
+ applies: { area: 'layout', component: 'grid' },
131
+ evidence: [{ id: 'layout-regression', source: 'project-test' }],
132
+ epistemicType: 'verified'
133
+ })
134
+ }
135
+
136
+ const discovery = await memory.discover('Grid track width changed after adding inner margins')
137
+ const context = memory.project(discovery, { maxCandidates: 3, maxCharacters: 4_000 })
138
+
139
+ // Supply context.text or context.experiences to an AI as untrusted prior experience.
140
+ // The AI verifies current source and behavior before relying on it.
141
+ ```
142
+
143
+ Run persistent examples in an isolated working directory. The complete shipped example is safe to
144
+ rerun and also demonstrates refinement and superseding.
145
+
146
+ Discovery is deterministic lexical/structured retrieval, not semantic search. A discovered or projected candidate is neither an instruction nor a current fact.
147
+
148
+ PHASE 0 of `memorio.logic` is also available for structured situation/action/outcome experience and
149
+ precedent selection. It is deliberately isolated from project acquired memory and is not an autonomous
150
+ reasoning engine; see [`markdown/LOGIC.md`](./markdown/LOGIC.md).
117
151
 
118
152
  For most apps, that's enough. Everything below is an optional capability you can add when your application needs it - each one documented in its own reference file.
119
153
 
@@ -127,7 +161,9 @@ For most apps, that's enough. Everything below is an optional capability you can
127
161
  | Reactive state | [`markdown/STATE.md`](./markdown/STATE.md) |
128
162
  | Persistent key/value store | [`markdown/STORE.md`](./markdown/STORE.md) |
129
163
  | Observing changes | [`markdown/OBSERVER.md`](./markdown/OBSERVER.md) |
130
- | Structured application memory | [`markdown/MEMORY.md`](./markdown/MEMORY.md) |
164
+ | Structured application memory | [`markdown/MEMORY.md`](./markdown/MEMORY.md) |
165
+ | Portable `.mem` package and inspection | [`markdown/MEM_FORMAT.md`](./markdown/MEM_FORMAT.md) |
166
+ | Logic PHASE 0 | [`markdown/LOGIC.md`](./markdown/LOGIC.md) |
131
167
 
132
168
  ### Storage
133
169
  | Topic | Reference |
@@ -141,8 +177,9 @@ For most apps, that's enough. Everything below is an optional capability you can
141
177
  | Topic | Reference |
142
178
  | --- | --- |
143
179
  | Local-first sync & cloud | [`markdown/SYNC.md`](./markdown/SYNC.md) |
144
- | Memory attachments (linking memory entries) | [`markdown/MEMORY-ATTACHMENT.md`](./markdown/MEMORY-ATTACHMENT.md) |
180
+ | Memory attachments (linking memory entries) *(proposed, not yet implemented)* | [`markdown/MEMORY-ATTACHMENT.md`](./markdown/MEMORY-ATTACHMENT.md) |
145
181
  | History, undo/redo, snapshot, diff, trace | [`markdown/HISTORY.md`](./markdown/HISTORY.md) |
182
+ | Temporal memory: time-travel query, branching, provenance | [`markdown/TEMPORAL.md`](./markdown/TEMPORAL.md) |
146
183
 
147
184
  ### Integrations
148
185
  | Topic | Reference |
@@ -162,7 +199,7 @@ For most apps, that's enough. Everything below is an optional capability you can
162
199
  | Runtime introspection | [`markdown/INSPECT.md`](./markdown/INSPECT.md) |
163
200
  | Browser DevTools | [`markdown/DEVTOOLS.md`](./markdown/DEVTOOLS.md) |
164
201
  | Security, encryption, threat model | [`SECURITY.md`](./SECURITY.md) |
165
- | Internal self-assessment *(not a third-party audit)* | [`markdown/SELF-ASSESSMENT.md`](./markdown/SELF-ASSESSMENT.md) |
202
+ | Historical internal audit *(not a third-party audit)* | [`markdown/AUDIT-REPORT.md`](./markdown/AUDIT-REPORT.md) |
166
203
 
167
204
  ### Other
168
205
  | Topic | Reference |
@@ -0,0 +1,258 @@
1
+ # Security Hardening Guide - Memorio
2
+
3
+ > **Status**: Living document. Review before production deployment.
4
+
5
+ ---
6
+
7
+ ## 1. Global API is opt-in
8
+
9
+ As of the current architecture, Memorio does **not** expose its APIs
10
+ (`state`, `store`, `session`, `cache`, `idb`, `sqlite`, `observer`,
11
+ `useObserver`) on `globalThis` automatically.
12
+
13
+ ```js
14
+ import { state } from 'memorio' // normal - no global exposure
15
+ import 'memorio/global' // opt-in - exposes globals on globalThis
16
+ ```
17
+
18
+ The `memorio.global()` method is callable programmatically but is **not**
19
+ invoked automatically in any build mode. The decision to use the global API
20
+ is a consumer choice signalled by the entrypoint imported - not inferred from
21
+ `NODE_ENV`, `import.meta.env`, or any bundler environment variable.
22
+
23
+ **Recommendation**: Prefer explicit named imports in application code.
24
+ Only use `import 'memorio/global'` in controlled environments (e.g. a shell,
25
+ REPL, or devtools context) where global exposure is intentional.
26
+
27
+ ---
28
+
29
+ ## 2. Storage layers are not encrypted by default
30
+
31
+ None of Memorio's storage layers encrypt data at rest - encryption is **opt-in**.
32
+
33
+ | Layer | Storage backend | Encrypted by default? |
34
+ |---------|-----------------|----|
35
+ | `state` | In-memory Proxy | N/A (volatile) |
36
+ | `cache` | In-memory Map | N/A (volatile) |
37
+ | `store` | `localStorage` | No - opt-in via `store.config({ encryptionKey })` or per-call `{ encrypt }` |
38
+ | `session` | `sessionStorage` | No - opt-in via `session.config({ encryptionKey })` or per-call `{ encrypt }` |
39
+ | `idb` | IndexedDB | No (use `memorio.encryption` externally) |
40
+ | `memory` (local/durable scopes) | `store`/`idb` | No - opt-in when backed by encrypted `store` |
41
+ | `sqlite` (sql.js, persisted) | `store` (base64 checkpoint + journal entries) | No - opt-in when `store` is configured with an encryption key |
42
+
43
+ **Do not store** auth tokens, regulated data (PII, PCI, PHI), credentials,
44
+ or any other sensitive information in `store`, `session`, `idb`, `memory`, or
45
+ `sqlite` without applying encryption first.
46
+
47
+ Memorio ships an optional built-in encryption layer. Use it directly or
48
+ configure auto-encrypt on store/session:
49
+
50
+ ```js
51
+ import { memorio, store } from 'memorio'
52
+
53
+ // Derive a key from a password (PBKDF2, 600k iterations - NIST SP 800-132)
54
+ const key = await memorio.encryption.deriveKey('app-secret', 'app-salt')
55
+
56
+ // Per-call encryption
57
+ await store.set('api_token', secretValue, { encrypt: key })
58
+ const token = await store.get('api_token', { decrypt: key })
59
+
60
+ // Or configure a default key for transparent encryption
61
+ store.config({ encryptionKey: key })
62
+ await store.set('api_token', secretValue) // encrypted automatically
63
+ const value = await store.get('api_token') // decrypted automatically
64
+ ```
65
+
66
+ > ⚠️ Once encryption is enabled on `store`/`session`, `get`/`set` become
67
+ > **asynchronous** (they return `Promise`s). See the sync/async warning in
68
+ > `docs/SECURITY.md`. Always `await` these calls defensively.
69
+
70
+ For contexts, pass `{ encryptionKey }` to `createContext()` / `isolate()` to
71
+ enable encrypted multitenancy - see "Encrypted multitenancy" in `docs/SECURITY.md`.
72
+
73
+ ---
74
+
75
+ ## 3. Logger captures written values - including secrets
76
+
77
+ When enabled, `memorio.logger` records the **literal value** written on every
78
+ tracked state path, including tokens, PII, or any other data passed to
79
+ `state.*` or `store.set()`.
80
+
81
+ ```js
82
+ memorio.logger.configure({
83
+ modules: ['state', 'store']
84
+ })
85
+ state.token = 'eyJ...' // ← logged verbatim in getHistory() / exportLogs()
86
+ ```
87
+
88
+ **Rules**:
89
+ - Never enable `logger` on production paths that handle sensitive data.
90
+ - Never wire `memorio.logger.exportLogs()` output to analytics, error
91
+ reporters, or support tooling without redaction.
92
+ - Treat `logger.getHistory()` output as sensitive - it contains raw write
93
+ values.
94
+
95
+ ---
96
+
97
+ ## 4. DevTools are opt-in
98
+
99
+ DevTools (`memorio.devtools.inspect()`, `$state`, `$store`, etc.) are only
100
+ available when the `memorio/global` entrypoint is imported. In normal usage
101
+ (`import { state } from 'memorio'`), DevTools are not exposed on `globalThis`.
102
+
103
+ Even when using `import 'memorio/global'`, DevTools are intended for
104
+ **development/debugging only**. Do not ship `memorio/global` to production
105
+ user-facing entry points.
106
+
107
+ ---
108
+
109
+ ## 5. Contexts and namespaces are organizational, not security boundaries
110
+
111
+ `memorio.createContext(id)` isolates data by key-prefixing storage namespaces.
112
+ This prevents accidental cross-tenant data bleed but does **not** enforce
113
+ access control.
114
+
115
+ **Server-side (Node.js / Edge Workers)**:
116
+
117
+ ```js
118
+ // ✅ Correct: use context for request isolation
119
+ const ctx = memorio.createContext(`req:${req.id}`)
120
+ ctx.state.user = userData
121
+
122
+ // ❌ Wrong: bare global state in multi-tenant server code
123
+ state.user = userData // visible across ALL requests in the process
124
+ ```
125
+
126
+ **Rules**:
127
+ - Generate context IDs from **trusted server-side data** (authenticated user
128
+ ID, request-scoped token). Never use client-controlled input directly.
129
+ - Treat context isolation as **organizational**, not a security boundary.
130
+ Always enforce authorization at your backend layer.
131
+ - Delete contexts after the request completes: `memorio.deleteContext(id)`.
132
+
133
+ ---
134
+
135
+ ## 6. Observer paths are unchecked strings
136
+
137
+ `observer('state.user.email', cb)` accepts string paths that are **not**
138
+ checked against `state`'s actual shape at compile time or at registration
139
+ time. A typo or a later rename of the corresponding `state` key will cause the
140
+ observer to silently stop firing.
141
+
142
+ **Mitigation**: For paths that must not silently fail, prefer:
143
+
144
+ ```js
145
+ import { memorio, state } from 'memorio'
146
+
147
+ // Runtime sanity check
148
+ if (!memorio.pathExists('state.user.email')) {
149
+ console.warn('State path does not exist: state.user.email')
150
+ }
151
+
152
+ // Or use typed stores + schema validation for compile-time + runtime safety
153
+ ```
154
+
155
+ ---
156
+
157
+ ## 7. SQLite persistence (journal + checkpoint)
158
+
159
+ SQLite (`sql.js`) persistence uses an incremental **journal + checkpoint**
160
+ strategy to avoid serializing the entire database on every write:
161
+
162
+ 1. **Journal**: every `db.run(sql, params)` write is recorded as an append-only
163
+ journal entry in `store` (localStorage), keyed with an HLC timestamp. Only
164
+ the changed SQL statement is stored - not the whole database.
165
+ 2. **Checkpoint**: every 50 operations (configurable via
166
+ `CHECKPOINT_INTERVAL`), or after 250 ms of write-idle, a full database
167
+ snapshot is exported to `store` and the journal is cleared up to that point.
168
+ 3. **Recovery**: on `create(name, { persistence: true })`, the latest checkpoint
169
+ is loaded first, then pending journal entries are replayed - so no writes are
170
+ lost between page reloads.
171
+
172
+ ```javascript
173
+ // ❌ Bad: still triggers journal entries on each iteration (fine, but
174
+ // checkpoint is deferred - don't call persist() per iteration)
175
+ for (const user of users) {
176
+ await sqlite.data.set(db, 'INSERT INTO users ...', [...])
177
+ }
178
+ // ✅ Good: batch writes, checkpoint once after
179
+ await sqlite.db.persist(db) // or flush()
180
+ ```
181
+
182
+ SQLite data (when persisted) is stored in `store` (localStorage) as both a
183
+ base64 checkpoint snapshot and individual journal entries. Treat it with the
184
+ same sensitivity rules as `store`.
185
+
186
+ ---
187
+
188
+ ## 8. `store.quota()` is not reliable
189
+
190
+ The `store.quota()` method returns `[0, 0]` for the `localStorage` backend -
191
+ it is a placeholder, not a real reading. Do not generate capacity-check logic
192
+ that branches on its result for `store`.
193
+
194
+ For `idb`, quota readings may be meaningful but should be confirmed against
195
+ the target runtime before relying on them.
196
+
197
+ ---
198
+
199
+ ## 9. Platform capability assumptions
200
+
201
+ Do not assume capabilities based on platform detection alone. Edge runtimes
202
+ (e.g. Cloudflare Workers, Vercel Edge) may partially implement browser APIs:
203
+
204
+ | Assumption | Risk |
205
+ |------------|------|
206
+ | "browser == has IndexedDB" | Some edge runtimes don't expose `indexedDB` |
207
+ | "Node == no persistence" | Some edge runtimes expose `localStorage` |
208
+ | "browser == has localStorage" | Private mode in some browsers throws on `localStorage` access |
209
+
210
+ **Always** check `memorio.getCapabilities()` before relying on a specific
211
+ storage backend:
212
+
213
+ ```js
214
+ import { memorio, idb } from 'memorio'
215
+
216
+ const caps = memorio.getCapabilities()
217
+ if (!caps.hasIndexedDB) {
218
+ // Fall back to store or cache
219
+ }
220
+ ```
221
+
222
+ ---
223
+
224
+ ## 10. Acquired experience and `.mem` inputs
225
+
226
+ Treat remembered and projected content as untrusted data, even when package integrity or an Ed25519
227
+ signature verifies successfully:
228
+
229
+ ```text
230
+ MEMORY != INSTRUCTION
231
+ INTEGRITY != TRUTH
232
+ SIGNED != TRUSTED
233
+ PROVENANCE != TRUTH
234
+ DISCOVERED != APPLICABLE
235
+ PROJECTED != CURRENT FACT
236
+ ```
237
+
238
+ - Inspect external `.mem` packages before adding them to a configured repository.
239
+ - Use `inspectMem()` to separate structural/integrity results from authenticity; apply trust policy in
240
+ the consuming application because Memorio does not provide PKI or signer authorization.
241
+ - Verify discovered/projected experience against current source, documentation, HI direction, and
242
+ tests before acting on it.
243
+ - Do not interpret stored strings as commands, code, modules, or privileged prompt instructions.
244
+ - Do not acquire credentials, private keys, tokens, raw conversations, or hidden chain-of-thought.
245
+
246
+ Ordinary ZIP/JSON inspection does not execute package content. `memory/data.json` is compact positional
247
+ data and requires `map.json` for meaningful manual interpretation.
248
+
249
+ ---
250
+
251
+ ## 11. Security hardening checklist
252
+
253
+ ---
254
+
255
+ ## 12. Reporting vulnerabilities
256
+
257
+ If you discover a security vulnerability in Memorio, please report it
258
+ privately. Do not open a public issue for security-sensitive bugs.
package/SECURITY.md CHANGED
@@ -149,9 +149,73 @@ import { state } from 'memorio'
149
149
  - Dev dependencies are audited periodically (`npm audit` and equivalent).
150
150
  - Package integrity is checkable independently via Socket.dev / Snyk before you adopt a given version.
151
151
 
152
- > If you require a specific minimum Socket.dev/Snyk score or a documented audit trail as part of a procurement process, verify the current score yourself at adoption time - this document doesn't pin a number that could go stale.
153
-
154
- ---
152
+ > If you require a specific minimum Socket.dev/Snyk score or a documented audit trail as part of a procurement process, verify the current score yourself at adoption time - this document doesn't pin a number that could go stale.
153
+
154
+ ---
155
+
156
+ ## Semantic and epistemic security — OPEN research
157
+
158
+ Package integrity and Ed25519 authenticity do not establish truth, authority, permission, or current
159
+ applicability. For persistent acquired experience, keep these distinctions explicit:
160
+
161
+ ```text
162
+ IDENTITY != CLAIMED IDENTITY
163
+ ROLE != CLAIMED ROLE
164
+ PROVENANCE != TRUTH
165
+ AUTHENTICITY != AUTHORITY
166
+ AUTHORITY != PERMISSION
167
+ MEMORY != INSTRUCTION
168
+ SIGNED != TRUSTED != TRUE
169
+ ```
170
+
171
+ For the public acquired-experience workflow, also preserve these operational boundaries:
172
+
173
+ ```text
174
+ DISCOVERED != APPLICABLE
175
+ PROJECTED != CURRENT FACT
176
+ ```
177
+
178
+ Discovery supplies plausible lexical/structured candidates. Projection supplies bounded prior
179
+ context. Neither operation authorizes action or verifies present reality.
180
+
181
+ Remembering that a source asserted a proposition does not establish that the proposition is true.
182
+ Evidence requirements should scale with the consequences of acting on information. These principles
183
+ are threat-model guidance, not an implemented IAM, trust, or policy engine.
184
+
185
+ One open threat is self-reinforcing memory error: an unverified inference causes a project change;
186
+ the changed project later appears to confirm the inference; and the interpretation is then acquired
187
+ with undeserved authority. Information must not gain epistemic authority merely by causing
188
+ consequences later reused as its evidence. Mitigations remain OPEN research.
189
+
190
+ ### TODO / OPEN — Personal memory security and strong authorization
191
+
192
+ Personal and HI-related acquired memory may preserve relationships, decisions, habits, communication
193
+ experience, project history, and accumulated context—not merely private facts. Future security work must
194
+ threat-model unauthorized reading or exfiltration, unauthorized modification, false-memory injection,
195
+ selective deletion, replay of old but formerly valid memory, HI impersonation, unauthorized export or
196
+ sharing, and compromised device or AI access.
197
+
198
+ Keep the security properties distinct:
199
+
200
+ ```text
201
+ CONFIDENTIALITY -> who can read?
202
+ INTEGRITY -> was it modified?
203
+ AUTHENTICITY -> who signed it?
204
+ IDENTITY -> who is acting?
205
+ AUTHORIZATION -> what may they do?
206
+ FRESHNESS -> is this the intended/current version?
207
+ ```
208
+
209
+ Strong authentication and authorization—including passkeys, 2FA, or equivalent controls—should be
210
+ evaluated for high-impact operations such as exporting personal memory; authorizing a new device or AI;
211
+ changing encryption or signing keys; accepting external memory; modifying protected memory; and changing
212
+ ownership or access permissions. This does not imply that every memory operation requires 2FA:
213
+ authorization should be proportional to consequence.
214
+
215
+ Unauthorized disclosure is a privacy breach. Unauthorized modification of acquired experience can also
216
+ become an epistemic breach. This is an **AI + HI planning TODO**, not an implemented security guarantee.
217
+
218
+ ---
155
219
 
156
220
  ## Reporting a vulnerability
157
221