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.
- package/AGENTS.md +21 -12
- package/CHANGELOG.md +18 -6
- package/README.md +44 -7
- package/SECURITY-HARDENING.md +258 -0
- package/SECURITY.md +67 -3
- package/SUMMARY.md +55 -59
- package/adr/002-observer-semantics.md +1 -1
- package/adr/003-deep-mutation-semantics.md +1 -1
- package/adr/004-array-mutation-semantics.md +1 -1
- package/adr/010-logic-phase-0.md +42 -0
- package/adr/README.md +16 -11
- package/bin/cli.js +82 -60
- package/examples/acquired-knowledge.ts +174 -0
- package/examples/agent-memory-demo.ts +140 -0
- package/examples/sync.ts +90 -90
- package/examples/useObserver.tsx +2 -2
- package/global.cjs +1995 -124
- package/global.js +1990 -125
- package/index.cjs +1995 -124
- package/index.d.ts +1 -0
- package/index.js +1990 -125
- package/llms.txt +122 -4
- package/markdown/EXTENSION_VSCODE_DESIGN.md +410 -0
- package/markdown/LOGIC.md +100 -0
- package/markdown/MEMORY-ATTACHMENT.md +17 -10
- package/markdown/MEMORY.md +378 -15
- package/markdown/MEM_FORMAT.md +313 -0
- package/markdown/STATE.md +27 -3
- package/markdown/SYNC.md +18 -14
- package/markdown/TEMPORAL.md +297 -0
- package/markdown/USEOBSERVER.md +7 -4
- package/modules/redux.cjs +1159 -32
- package/modules/redux.cjs.map +1 -1
- package/modules/redux.js +1158 -32
- package/modules/redux.js.map +1 -1
- package/package.json +14 -5
- package/types/exports.d.ts +47 -3
- package/types/logic.d.ts +79 -0
- package/types/memorio.d.ts +60 -16
- package/types/memory.d.ts +118 -0
- package/types/session.d.ts +1 -4
- package/types/store.d.ts +1 -4
- package/types/temporal.d.ts +95 -0
- package/types/useObserver.d.ts +6 -10
- package/vsix/memorio.vsix +0 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
# Memorio `.mem` Format
|
|
2
|
+
|
|
3
|
+
This document describes the implemented MEM package and `memorio.knowledge` logical format. A `.mem`
|
|
4
|
+
is data. It is not a program, module, prompt, or trusted instruction source.
|
|
5
|
+
|
|
6
|
+
> **ZIP is packaging. JSON is inspection. MAP is meaning. MEM is experience.**
|
|
7
|
+
|
|
8
|
+
Memorio must not require trust in Memorio to inspect a Memorio file. Ordinary ZIP and JSON tools can
|
|
9
|
+
open and inspect the package; opening or extracting it does not execute its contents.
|
|
10
|
+
|
|
11
|
+
## Package
|
|
12
|
+
|
|
13
|
+
Current Memorio writes `.mem` as a standard ZIP archive with four ordinary JSON parts:
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
project.mem
|
|
17
|
+
├── manifest.json
|
|
18
|
+
├── map.json
|
|
19
|
+
├── memory/data.json
|
|
20
|
+
└── security/integrity.json
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
No empty future directories are created. Protected unknown files under `extensions/` are accepted and
|
|
24
|
+
preserved without interpretation. All other unexpected or unprotected entries are rejected.
|
|
25
|
+
|
|
26
|
+
The archive can be listed or extracted with normal ZIP tools. `manifest.json` identifies
|
|
27
|
+
`memorio.package` version 1 and locates the other standard parts. `map.json` and `memory/data.json`
|
|
28
|
+
contain the same logical v2 map and data described below. ZIP is packaging; MEM semantics do not
|
|
29
|
+
depend on ZIP entry metadata.
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"format": "memorio.package",
|
|
34
|
+
"version": 1,
|
|
35
|
+
"memory": { "format": "memorio.knowledge", "version": 2 },
|
|
36
|
+
"parts": {
|
|
37
|
+
"map": "map.json",
|
|
38
|
+
"data": "memory/data.json",
|
|
39
|
+
"integrity": "security/integrity.json"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
For a Memorio diagnostic view, run:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npx memorio inspect project.mem
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Integrity
|
|
51
|
+
|
|
52
|
+
`security/integrity.json` uses SHA-256 and records a `sha256:<hex>` digest for `manifest.json`,
|
|
53
|
+
`map.json`, `memory/data.json`, and every preserved extension part. It does not hash itself, avoiding
|
|
54
|
+
a recursive self-hash. Every non-integrity archive entry must appear exactly once in the protected
|
|
55
|
+
files table, and every recorded digest must match.
|
|
56
|
+
|
|
57
|
+
`contentId` is the SHA-256 digest of the canonical, path-sorted protected-files digest table. It is a
|
|
58
|
+
deterministic content/cache identity, not the hash of the ZIP bytes. It does not identify an author,
|
|
59
|
+
prove trust, certify content, or constitute a digital signature.
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"algorithm": "sha256",
|
|
64
|
+
"files": {
|
|
65
|
+
"manifest.json": "sha256:...",
|
|
66
|
+
"map.json": "sha256:...",
|
|
67
|
+
"memory/data.json": "sha256:..."
|
|
68
|
+
},
|
|
69
|
+
"contentId": "sha256:..."
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Administrators can extract the package, inspect its JSON, and verify each listed digest with standard
|
|
74
|
+
SHA-256 tooling. The exact command differs by operating system; hash the raw bytes of each extracted
|
|
75
|
+
part and compare the lowercase hexadecimal digest with its integrity entry.
|
|
76
|
+
|
|
77
|
+
Package generation sorts entry paths, recursively sorts JSON object keys, retains array order, uses
|
|
78
|
+
two-space JSON indentation with a final newline, fixes ZIP timestamps to 1980-01-02 UTC, and uses
|
|
79
|
+
DEFLATE level 6. Equivalent supported logical content therefore produces deterministic package bytes.
|
|
80
|
+
|
|
81
|
+
### Defensive Limits
|
|
82
|
+
|
|
83
|
+
- Maximum package size: 10 MiB.
|
|
84
|
+
- Maximum uncompressed total: 25 MiB.
|
|
85
|
+
- Maximum individual entry: 8 MiB.
|
|
86
|
+
- Maximum entries: 128.
|
|
87
|
+
- Maximum declared expansion ratio per entry: 200:1.
|
|
88
|
+
|
|
89
|
+
Readers reject unsafe absolute/traversal paths, backslash paths, duplicate entries, directory entries,
|
|
90
|
+
unsupported compression, malformed ZIP/UTF-8/JSON, missing or unprotected parts, unsupported package
|
|
91
|
+
or integrity versions, and checksum mismatches. Archives are read in memory without filesystem
|
|
92
|
+
extraction. Nested archive data is never recursively opened.
|
|
93
|
+
|
|
94
|
+
Integrity proves only that protected bytes match the package's digest table. Because an attacker can
|
|
95
|
+
replace content and recompute unsigned hashes, integrity is not authenticity, signature, certification,
|
|
96
|
+
authorization, safety, or trust. Implemented optional signatures are separate security parts and do
|
|
97
|
+
not change this distinction.
|
|
98
|
+
|
|
99
|
+
### Optional Ed25519 Signatures
|
|
100
|
+
|
|
101
|
+
Unsigned packages remain valid. An explicitly signed package adds one JSON document per signing key:
|
|
102
|
+
|
|
103
|
+
```text
|
|
104
|
+
security/signatures/<64-character-public-key-fingerprint>.json
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The protected manifest also declares `"authenticity": { "signed": true }`. This makes simple
|
|
108
|
+
signature-file removal structurally invalid. Converting a signed package back to an unsigned package
|
|
109
|
+
requires rebuilding the protected manifest and therefore produces a different `contentId`.
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"format": "memorio.signature",
|
|
114
|
+
"version": 1,
|
|
115
|
+
"algorithm": "Ed25519",
|
|
116
|
+
"contentId": "sha256:...",
|
|
117
|
+
"keyId": "sha256:...",
|
|
118
|
+
"publicKey": { "format": "raw", "encoding": "base64url", "value": "..." },
|
|
119
|
+
"signature": { "encoding": "base64url", "value": "..." }
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
The signed bytes are the following domain-separated UTF-8 payload:
|
|
124
|
+
|
|
125
|
+
```text
|
|
126
|
+
MEMORIO-PACKAGE-SIGNATURE-V1\n<contentId>\n
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Signing the domain-separated `contentId` binds the signature to the canonical protected-parts digest
|
|
130
|
+
table without binding it to incidental ZIP bytes. Signature documents are outside both the protected
|
|
131
|
+
digest table and `contentId`, preventing signature recursion. Protected extensions are already part of
|
|
132
|
+
that table and are therefore authenticated even when Memorio does not understand them.
|
|
133
|
+
|
|
134
|
+
Ed25519 was selected for compact keys and signatures, deterministic signatures, standard raw public
|
|
135
|
+
keys, and Web Crypto support in the supported modern Node/browser path. The signing API accepts
|
|
136
|
+
external Ed25519 `CryptoKey` objects. Public verification material is stored as a 32-byte raw key.
|
|
137
|
+
`keyId` is the SHA-256 fingerprint of those raw public-key bytes. Private keys remain external and are
|
|
138
|
+
never serialized into `.mem`.
|
|
139
|
+
|
|
140
|
+
Verification validates package structure and limits, verifies SHA-256 integrity and reconstructs
|
|
141
|
+
`contentId`, and only then validates and verifies each signature. Inspection reports authenticity
|
|
142
|
+
separately as `unsigned`, `valid`, `invalid`, or `unsupported`, with `trust: not_evaluated`. A valid
|
|
143
|
+
signature proves control of the corresponding private key over that content identity. It does not
|
|
144
|
+
establish a person, organization, certification, authorization, safety, claim correctness, or trust.
|
|
145
|
+
|
|
146
|
+
Ordinary memory updates produce a new `contentId` and omit all prior signatures. Memorio never
|
|
147
|
+
automatically re-signs. Explicit signing is available through:
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
const signed = await signMemPackage(unsignedPackage, privateCryptoKey, publicCryptoKey)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
The same protected content and Ed25519 key produce deterministic signature metadata and package bytes.
|
|
154
|
+
Multiple signature entry paths are structurally supported, but signer policy, trust, revocation, and
|
|
155
|
+
certification are not implemented.
|
|
156
|
+
|
|
157
|
+
## Logical v2 Envelope
|
|
158
|
+
|
|
159
|
+
Before packaging, the logical memory has this version 2 envelope:
|
|
160
|
+
|
|
161
|
+
```json
|
|
162
|
+
{
|
|
163
|
+
"format": "memorio.knowledge",
|
|
164
|
+
"version": 2,
|
|
165
|
+
"map": {},
|
|
166
|
+
"data": []
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
`format` identifies the file family. `version` selects its codec. `map` declares how positional
|
|
171
|
+
records are interpreted. `data` contains the source's knowledge version and claims.
|
|
172
|
+
|
|
173
|
+
Version 1 used a `knowledge` object shaped like the runtime `KnowledgeState`. Current Memorio detects
|
|
174
|
+
content rather than relying on the `.mem` extension: it reads v1 JSON, v2 JSON, and package v1, while
|
|
175
|
+
new repository writes use package v1 containing logical v2. Reading historical JSON does not rewrite
|
|
176
|
+
it; a later explicit memory update writes the current package representation. Unsupported or corrupt
|
|
177
|
+
input is rejected with a diagnostic status.
|
|
178
|
+
|
|
179
|
+
## MAP
|
|
180
|
+
|
|
181
|
+
A v2 map declares semantic identity separately from encoding position:
|
|
182
|
+
|
|
183
|
+
```json
|
|
184
|
+
{
|
|
185
|
+
"state": ["knowledge-version", "claims"],
|
|
186
|
+
"claim": ["id", "knowledge", "applicability", "evidence", "epistemic-type", "provenance", "acquired-at", "revision"],
|
|
187
|
+
"evidence": ["id", "source", "observed-at", "supports"],
|
|
188
|
+
"provenance": ["source", "detail"],
|
|
189
|
+
"revision": ["kind", "claim-ids", "reason"],
|
|
190
|
+
"epistemicType": ["observed", "inferred", "human-stated", "verified"],
|
|
191
|
+
"revisionKind": ["refines", "supersedes", "retracts"]
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
For example, a claim value at position `0` means `id` only when `map.claim[0]` is `id`. Readers look
|
|
196
|
+
up positions through the source-local map. Field and vocabulary order is not permanent.
|
|
197
|
+
|
|
198
|
+
The map is an interpretation contract and a compact vocabulary declaration. It is not executable,
|
|
199
|
+
an ontology, a synonym table, or authority to reinterpret historical meanings.
|
|
200
|
+
|
|
201
|
+
## DATA
|
|
202
|
+
|
|
203
|
+
`data` is a positional state record interpreted through `map.state`. Its `claims` value contains
|
|
204
|
+
positional claim records interpreted through `map.claim`. Evidence, provenance, and revision records
|
|
205
|
+
use their corresponding maps. Optional trailing `null` positions may be omitted.
|
|
206
|
+
|
|
207
|
+
Vocabulary values are zero-based positions in the source-local `epistemicType` and `revisionKind`
|
|
208
|
+
arrays. For example, with the map above, epistemic value `3` means `verified`.
|
|
209
|
+
|
|
210
|
+
The current `memory/data.json` is therefore inspectable but deliberately compact: positional arrays
|
|
211
|
+
and numeric enum indexes are not maximally human-readable in isolation. Meaningful manual inspection
|
|
212
|
+
requires cross-referencing `map.json`; `memory/data.json` alone is not self-explanatory.
|
|
213
|
+
|
|
214
|
+
```json
|
|
215
|
+
{
|
|
216
|
+
"format": "memorio.knowledge",
|
|
217
|
+
"version": 2,
|
|
218
|
+
"map": {
|
|
219
|
+
"state": ["knowledge-version", "claims"],
|
|
220
|
+
"claim": ["id", "knowledge", "applicability", "evidence", "epistemic-type", "provenance", "acquired-at", "revision"],
|
|
221
|
+
"evidence": ["id", "source", "observed-at", "supports"],
|
|
222
|
+
"provenance": ["source", "detail"],
|
|
223
|
+
"revision": ["kind", "claim-ids", "reason"],
|
|
224
|
+
"epistemicType": ["observed", "inferred", "human-stated", "verified"],
|
|
225
|
+
"revisionKind": ["refines", "supersedes", "retracts"]
|
|
226
|
+
},
|
|
227
|
+
"data": [1, [["layout.rule", "Use layout tokens.", {"area": "layout"}, [["review-1"]], 3]]]
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Implemented Semantics
|
|
232
|
+
|
|
233
|
+
- State: knowledge version and ordered append-only claims.
|
|
234
|
+
- Claim: identity, JSON knowledge, applicability, evidence, epistemic type, provenance, acquisition
|
|
235
|
+
time, and an optional revision relationship.
|
|
236
|
+
- Evidence: identity, source, observation time, and bounded `supports` text.
|
|
237
|
+
- Epistemic types: `observed`, `inferred`, `human-stated`, and `verified`.
|
|
238
|
+
- Revision kinds: `refines`, `supersedes`, and `retracts`.
|
|
239
|
+
|
|
240
|
+
Claim identity and source-file provenance are distinct. Source provenance is established by the file
|
|
241
|
+
that contributes a claim; claim provenance is claim metadata.
|
|
242
|
+
|
|
243
|
+
## Known, Unknown, and Invalid
|
|
244
|
+
|
|
245
|
+
Known semantics are decoded into `KnowledgeState`, validated, and available to selection.
|
|
246
|
+
|
|
247
|
+
Unknown but valid mapped fields and vocabulary values remain opaque JSON data. They are retained with
|
|
248
|
+
their source-local map and positions, excluded from `KnowledgeState`, and not used for selection or
|
|
249
|
+
reasoning. When the same source is rewritten, the current writer overlays known fields and writes the
|
|
250
|
+
unknown data back unchanged. New claims leave unknown positions empty.
|
|
251
|
+
|
|
252
|
+
Invalid data is rejected. This includes malformed envelopes, missing required known meanings,
|
|
253
|
+
duplicate map identities, invalid positional records, non-JSON values, invalid known claims, and
|
|
254
|
+
unsupported format versions. Preservation does not turn invalid data into an unknown extension.
|
|
255
|
+
|
|
256
|
+
## Compatibility and Evolution
|
|
257
|
+
|
|
258
|
+
Each `.mem` source carries its own version and interpretation map and is decoded independently.
|
|
259
|
+
Multiple sources need not have identical field order, vocabulary order, creation time, or producer.
|
|
260
|
+
Historical files therefore retain the information needed to interpret their original representation.
|
|
261
|
+
|
|
262
|
+
The source-local map is not declared to be the final or only interpretation layer. A future system
|
|
263
|
+
may build a separate dynamic map or index across many memories. Local maps remain valuable in that
|
|
264
|
+
architecture: individual sources stay independently interpretable and can contribute the declarations
|
|
265
|
+
needed to reconstruct a damaged or missing aggregate map. No global-map filename, schema, authority,
|
|
266
|
+
or runtime behavior is defined by v2.
|
|
267
|
+
|
|
268
|
+
A new representation does not necessarily invalidate or supersede an old representation. Different
|
|
269
|
+
labels may remain useful for the same underlying concept in different contexts. The format does not
|
|
270
|
+
perform synonym resolution, ontology translation, or automatic supersedence.
|
|
271
|
+
|
|
272
|
+
The opaque-preservation rule permits an older runtime or future independent editor to retain valid
|
|
273
|
+
extensions it does not understand. Actual composition, relationship editing, and plugin lifecycle
|
|
274
|
+
behavior are not part of this format version.
|
|
275
|
+
|
|
276
|
+
## Composition and Reuse Readiness
|
|
277
|
+
|
|
278
|
+
A memory may eventually be used as a reusable, data-only memory plugin or as a component of a larger
|
|
279
|
+
memory. Version 2 does not define mounting, discovery, certification, permissions, dependencies, or
|
|
280
|
+
plugin execution.
|
|
281
|
+
|
|
282
|
+
Future composition is not assumed to copy complete input files into an output file. A derived memory
|
|
283
|
+
may instead record dependencies or lineage to independently retained sources, allowing a changed input
|
|
284
|
+
to trigger reevaluation rather than automatic copying or replacement. The identity, integrity, trust,
|
|
285
|
+
conflict, and lineage model required for this behavior remains deliberately unspecified.
|
|
286
|
+
|
|
287
|
+
The format likewise does not declare differently named concepts equivalent. An AI or another future
|
|
288
|
+
tool may propose relationships using the memories and current context, but the codec performs no
|
|
289
|
+
semantic discovery. New vocabulary does not automatically supersede old vocabulary, and a complete
|
|
290
|
+
memory may later be treated as one component of a larger memory.
|
|
291
|
+
|
|
292
|
+
These constraints keep the representation independent from any future editor technology. An editor,
|
|
293
|
+
CLI, library, AI, or server tool should operate on the same data and interpretation contracts.
|
|
294
|
+
|
|
295
|
+
## Security
|
|
296
|
+
|
|
297
|
+
All `.mem` content is untrusted data. A reader must not evaluate values, construct functions from
|
|
298
|
+
them, execute expressions or commands, import modules named by them, or treat stored knowledge as
|
|
299
|
+
trusted instructions. Unknown preservation carries JSON data forward; it grants no execution or
|
|
300
|
+
authority.
|
|
301
|
+
|
|
302
|
+
## Verified production inspection
|
|
303
|
+
|
|
304
|
+
Before the 5.2.0 release checkpoint, a representative package generated through the production
|
|
305
|
+
acquisition path was inspected with ordinary ZIP/JSON tooling and `inspectMem()`. It was 1,712 bytes
|
|
306
|
+
with SHA-256 `568cd4a309f753bb8179d756849a2bcb403ced2eb62fcf8df8ee6448b9c83335` and contained the four
|
|
307
|
+
standard unsigned entries shown above. Inspection reported package v1, memory v2, valid integrity,
|
|
308
|
+
no unknown semantics, and `unsigned` / `not_evaluated` authenticity. A field-by-field semantic
|
|
309
|
+
round-trip preserved both synthetic representative claims, including evidence, provenance,
|
|
310
|
+
applicability, epistemic type, timestamps, and a `refines` relationship.
|
|
311
|
+
|
|
312
|
+
That synthetic artifact is verification evidence, not normative production data or a fixture that
|
|
313
|
+
applications must reproduce byte-for-byte.
|
package/markdown/STATE.md
CHANGED
|
@@ -100,15 +100,39 @@ console.debug(protect); // Array of protected keys
|
|
|
100
100
|
|
|
101
101
|
### Lock
|
|
102
102
|
|
|
103
|
+
`state` exposes two lock scopes with distinct method names.
|
|
104
|
+
|
|
105
|
+
**Global lock** - freeze the entire `state` tree at once:
|
|
106
|
+
|
|
107
|
+
```javascript
|
|
108
|
+
state.lock();
|
|
109
|
+
state.user = { name: 'Sara' }; // Error: state is locked
|
|
110
|
+
state.user.name = 'Luigi'; // Error: state is locked
|
|
111
|
+
state.lock(); // (safe to call again)
|
|
112
|
+
state.unlock(); // resume all writes
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Per-key lock** - freeze a single first-level node:
|
|
116
|
+
|
|
103
117
|
```javascript
|
|
104
|
-
// Lock an object or array
|
|
105
118
|
state.myArray = [1, 2, 3];
|
|
106
119
|
state.myArray.lock();
|
|
107
120
|
|
|
108
|
-
//
|
|
109
|
-
|
|
121
|
+
// While locked, reassigning the key, mutating the node, array methods,
|
|
122
|
+
// and deleting its properties all fail:
|
|
123
|
+
state.myArray.push(4); // Error: state 'myArray' is locked
|
|
124
|
+
state.myArray[0] = 9; // Error: state 'myArray' is locked
|
|
125
|
+
delete state.myArray[0]; // Error: state 'myArray' is locked
|
|
126
|
+
state.myArray = []; // Error: state 'myArray' is locked
|
|
127
|
+
|
|
128
|
+
state.myArray.unlock(); // Resume writes to myArray only
|
|
110
129
|
```
|
|
111
130
|
|
|
131
|
+
Per-key locking is **first-level only**: `state.a.lock()` does not transitively lock
|
|
132
|
+
`state.a.b`. Lock each descendant explicitly (`state.a.b.lock()`), or use the global
|
|
133
|
+
`state.lock()` for whole-subtree immutability. While a key is per-key locked, every other
|
|
134
|
+
top-level key remains writable.
|
|
135
|
+
|
|
112
136
|
---
|
|
113
137
|
|
|
114
138
|
## How It Works
|
package/markdown/SYNC.md
CHANGED
|
@@ -48,18 +48,23 @@ Instead:
|
|
|
48
48
|
```ts
|
|
49
49
|
memorio.memory.remember('user.language', 'Italian')
|
|
50
50
|
// and a single configuration point:
|
|
51
|
-
memorio.memory.configure({
|
|
51
|
+
memorio.memory.configure({ provider: myCloudProvider, namespace: '…' })
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
## 2.
|
|
54
|
+
## 2. Namespace isolation (not a security boundary)
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
The journal is partitioned by a `namespace` string you pass to `configure()`.
|
|
57
|
+
All journal entries, pending operations, and replay calls for one namespace are
|
|
58
|
+
fully isolated from another - there is no API to enumerate or open a different
|
|
59
|
+
namespace's journal. You can use any naming convention, for example:
|
|
60
|
+
|
|
61
|
+
| Namespace pattern | Lifetime | Syncs by default |
|
|
57
62
|
|---|---|---|
|
|
58
|
-
| `
|
|
59
|
-
| `
|
|
60
|
-
| `
|
|
63
|
+
| `device:<id>` | this browser/device only | no (sticky) |
|
|
64
|
+
| `user:<id>:device:<id>` | follows the user across devices | yes (requires provider) |
|
|
65
|
+
| `tenant:<id>:user:<id>` | shared across users / tenant | yes (requires provider) |
|
|
61
66
|
|
|
62
|
-
> As with `memorio.createContext`, **scoping is a naming convention, not a
|
|
67
|
+
> As with `memorio.createContext`, **namespace scoping is a naming convention, not a
|
|
63
68
|
> security boundary.** Enforce real isolation server-side.
|
|
64
69
|
|
|
65
70
|
## 3. SQLite as the local durable store
|
|
@@ -87,7 +92,7 @@ because pending operations must survive a refresh for offline-first to work.
|
|
|
87
92
|
|
|
88
93
|
| Method | Returns | Notes |
|
|
89
94
|
|---|---|---|
|
|
90
|
-
| `memory.journal.append(entry, operation)` | `Promise<MemoryEntry>` | records `remember\|update\|forget\|expire\|confirm\|supersede` with `sync:'pending'` |
|
|
95
|
+
| `memory.journal.append(entry, operation)` | `Promise<MemoryEntry>` | records `remember\|update\|forget\|expire\|confirm\|supersede\|delete\|patch` with `sync:'pending'` |
|
|
91
96
|
| `memory.journal.pending()` | `Promise<MemoryEntry[]>` | rows where `sync != 'synced'`, for the current namespace |
|
|
92
97
|
| `memory.journal.markSynced(ids)` | `Promise<number>` | advances rows to `synced` (namespace-scoped) |
|
|
93
98
|
| `memory.journal.get(id)` | `Promise<MemoryEntry \| null>` | single entry, namespace-scoped |
|
|
@@ -113,7 +118,8 @@ The cloud must not simply say "last write wins." Memorio tags every entry with:
|
|
|
113
118
|
- `source` / `scope`
|
|
114
119
|
|
|
115
120
|
Remote conflicts are surfaced as `sync:'conflict'` rows via
|
|
116
|
-
`journal.pending()`; the provider's `
|
|
121
|
+
`journal.pending()`; the provider's `resolveConflict(local, remote)` hint decides
|
|
122
|
+
which entry wins. Example:
|
|
117
123
|
|
|
118
124
|
```
|
|
119
125
|
Laptop: language=Italian, confidence=0.92
|
|
@@ -132,7 +138,7 @@ memorio.memory.configure({
|
|
|
132
138
|
provider: {
|
|
133
139
|
push(ops) { return fetch('/api/sync', { method: 'POST', body: JSON.stringify(ops), headers: authHeaders }) }
|
|
134
140
|
pull(since) { return fetch(`/api/sync?since=${since}`).then(r => r.json()) }
|
|
135
|
-
|
|
141
|
+
resolveConflict(local, remote) { return (local.confidence ?? 0) >= 0.8 ? 'local' : 'remote' }
|
|
136
142
|
},
|
|
137
143
|
auto: true // auto-replay on focus/online (default true)
|
|
138
144
|
})
|
|
@@ -142,12 +148,10 @@ memorio.memory.configure({
|
|
|
142
148
|
interface SyncProvider {
|
|
143
149
|
push(ops: MemoryEntry[]): Promise<{ synced: string[]; conflicts?: string[]; error?: string }>
|
|
144
150
|
pull?(since?: number): Promise<MemoryEntry[]>
|
|
145
|
-
|
|
151
|
+
resolveConflict?(local: MemoryEntry, remote: MemoryEntry): Promise<'local' | 'remote' | 'merge'>
|
|
146
152
|
}
|
|
147
153
|
```
|
|
148
154
|
|
|
149
|
-
`memorio.memory.ready` resolves once the local journal substrate is chosen.
|
|
150
|
-
|
|
151
155
|
## 7. Security (NIST / OWASP / NSA posture)
|
|
152
156
|
|
|
153
157
|
- **Memorio never handles credentials.** No passwords, tokens, or API keys are
|
|
@@ -189,7 +193,7 @@ layered onto the existing journal **without** adopting a full CRDT framework
|
|
|
189
193
|
(Yjs, Automerge, etc.).
|
|
190
194
|
|
|
191
195
|
> **Scope note.** These strategies target `memorio.memory` first - it already
|
|
192
|
-
> carries the metadata a journal needs (`confidence`, `source`, `
|
|
196
|
+
> carries the metadata a journal needs (`confidence`, `source`, `tags`, `scope`,
|
|
193
197
|
> `createdAt`, `lastConfirmedAt`). If synchronization is ever extended to
|
|
194
198
|
> `state` or `store`, those layers must gain HLC timestamps and path-level
|
|
195
199
|
> fields explicitly - they cannot inherit them from `memory`.
|