@x12i/static-memorix 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +431 -0
- package/dist/cli.d.ts +17 -0
- package/dist/cli.js +278 -0
- package/dist/config.d.ts +39 -0
- package/dist/config.js +109 -0
- package/dist/engine/aliases.d.ts +32 -0
- package/dist/engine/aliases.js +74 -0
- package/dist/engine/associations.d.ts +26 -0
- package/dist/engine/associations.js +60 -0
- package/dist/engine/identity.d.ts +22 -0
- package/dist/engine/identity.js +50 -0
- package/dist/engine/inventory.d.ts +29 -0
- package/dist/engine/inventory.js +133 -0
- package/dist/engine/lists.d.ts +18 -0
- package/dist/engine/lists.js +62 -0
- package/dist/engine/narratives.d.ts +16 -0
- package/dist/engine/narratives.js +67 -0
- package/dist/engine/objectTypes.d.ts +11 -0
- package/dist/engine/objectTypes.js +71 -0
- package/dist/engine/query.d.ts +38 -0
- package/dist/engine/query.js +191 -0
- package/dist/engine/records.d.ts +23 -0
- package/dist/engine/records.js +152 -0
- package/dist/engine/snapshots.d.ts +17 -0
- package/dist/engine/snapshots.js +110 -0
- package/dist/engine/write.d.ts +30 -0
- package/dist/engine/write.js +163 -0
- package/dist/routes/helpers.d.ts +4 -0
- package/dist/routes/helpers.js +29 -0
- package/dist/routes/index.d.ts +2 -0
- package/dist/routes/index.js +402 -0
- package/dist/server.d.ts +15 -0
- package/dist/server.js +130 -0
- package/dist/storage/InMemoryStore.d.ts +31 -0
- package/dist/storage/InMemoryStore.js +242 -0
- package/dist/storage/fs.d.ts +6 -0
- package/dist/storage/fs.js +54 -0
- package/dist/types.d.ts +88 -0
- package/dist/types.js +37 -0
- package/guides/demo-app.md +527 -0
- package/guides/managing-json-files.md +384 -0
- package/guides/running-the-service.md +269 -0
- package/guides/using-the-api.md +265 -0
- package/mocks/data/admin/snapshots.json +52 -0
- package/mocks/data/assets/analysis.json +14 -0
- package/mocks/data/assets/decisions.json +14 -0
- package/mocks/data/assets/events.json +4 -0
- package/mocks/data/assets/snapshots.json +49 -0
- package/mocks/data/findings/analysis.json +4 -0
- package/mocks/data/findings/decisions.json +4 -0
- package/mocks/data/findings/events.json +3 -0
- package/mocks/data/findings/snapshots.json +21 -0
- package/mocks/data/marketing/snapshots.json +75 -0
- package/mocks/data/memory/memory.json +31 -0
- package/mocks/data/product/snapshots.json +84 -0
- package/mocks/data/system/inventory.json +8 -0
- package/mocks/data/users/snapshots.json +5 -0
- package/mocks/demo.html +828 -0
- package/mocks/metadata/agents.json +7 -0
- package/mocks/metadata/lists/assets-default.json +22 -0
- package/mocks/metadata/lists/backlog.json +10 -0
- package/mocks/metadata/lists/findings-default.json +9 -0
- package/mocks/metadata/lists/my-plate.json +10 -0
- package/mocks/metadata/lists/this-week.json +10 -0
- package/mocks/metadata/narratives/admin.json +7 -0
- package/mocks/metadata/narratives/assets.json +17 -0
- package/mocks/metadata/narratives/findings.json +7 -0
- package/mocks/metadata/narratives/marketing.json +17 -0
- package/mocks/metadata/narratives/product.json +12 -0
- package/mocks/metadata/object-types/admin.json +6 -0
- package/mocks/metadata/object-types/assets.json +9 -0
- package/mocks/metadata/object-types/findings.json +8 -0
- package/mocks/metadata/object-types/marketing.json +6 -0
- package/mocks/metadata/object-types/memory.json +6 -0
- package/mocks/metadata/object-types/product.json +6 -0
- package/mocks/metadata/object-types/users.json +13 -0
- package/mocks/metadata/write-descriptors/admin-task-write.json +20 -0
- package/mocks/metadata/write-descriptors/assets-analysis-write.json +14 -0
- package/mocks/metadata/write-descriptors/marketing-task-write.json +20 -0
- package/mocks/metadata/write-descriptors/memory-write.json +14 -0
- package/mocks/metadata/write-descriptors/product-task-write.json +20 -0
- package/package.json +67 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
# Managing JSON Files
|
|
2
|
+
|
|
3
|
+
## File-backed database routing
|
|
4
|
+
|
|
5
|
+
The static server has no database, so database routing is represented by a
|
|
6
|
+
reserved filename prefix followed by `--`.
|
|
7
|
+
|
|
8
|
+
With:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
MEMORIX_ORG_ID=neo
|
|
12
|
+
MEMORIX_AGENT_ID=neo-agent
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
the routing matrix is:
|
|
16
|
+
|
|
17
|
+
| Data kind | Filename prefix | Example |
|
|
18
|
+
| --- | --- | --- |
|
|
19
|
+
| Snapshots, analysis, decisions | `neo-memorix-entities--` | `data/product/neo-memorix-entities--snapshots.json` |
|
|
20
|
+
| Events | `neo-memorix-events--` | `data/product/neo-memorix-events--events.json` |
|
|
21
|
+
| Memory | `neo-agent-memorix-catalox--` | `data/memory/neo-agent-memorix-catalox--memory.json` |
|
|
22
|
+
| Metadata | `neo-agent-memorix-catalox--` | `metadata/lists/neo-agent-memorix-catalox--my-plate.json` |
|
|
23
|
+
|
|
24
|
+
### Read and write precedence
|
|
25
|
+
|
|
26
|
+
1. The server first looks for the routed, prefixed file.
|
|
27
|
+
2. If it does not exist, it reads the matching unprefixed file as seed data.
|
|
28
|
+
3. Any mutation writes the complete resulting collection to the routed file.
|
|
29
|
+
4. Unprefixed seed files are never overwritten while routed mode is active.
|
|
30
|
+
5. Files belonging to a different prefix are ignored by the active process.
|
|
31
|
+
|
|
32
|
+
Deleting a metadata document writes JSON `null` as a routed tombstone. This is
|
|
33
|
+
intentional: removing only the routed file would cause the unprefixed seed to
|
|
34
|
+
reappear on the next restart.
|
|
35
|
+
|
|
36
|
+
This makes onboarding migration-safe: existing files such as
|
|
37
|
+
`product/snapshots.json` continue to seed a new tenant, while the first write
|
|
38
|
+
creates `product/neo-memorix-entities--snapshots.json`. Starting with another
|
|
39
|
+
organization ID selects a different file and therefore different state.
|
|
40
|
+
|
|
41
|
+
The delimiter `--` is reserved. Do not use it inside ordinary, unprefixed
|
|
42
|
+
fixture basenames.
|
|
43
|
+
|
|
44
|
+
Routing IDs are validated before any files are opened. Organization IDs,
|
|
45
|
+
agent IDs, object types, and metadata names may contain letters, numbers,
|
|
46
|
+
dot, underscore, and hyphen, but cannot contain path separators, `..`, or the
|
|
47
|
+
reserved `--` delimiter.
|
|
48
|
+
|
|
49
|
+
Malformed or empty JSON stops startup with the affected path in the error;
|
|
50
|
+
it is never silently converted to an empty collection. Writes use a temporary
|
|
51
|
+
file and same-directory rename so readers never observe a partially written
|
|
52
|
+
JSON document.
|
|
53
|
+
|
|
54
|
+
Routing is process-scoped. Run one server process per organization/agent route,
|
|
55
|
+
and use only one writer process for a given prefix. Two processes writing the
|
|
56
|
+
same routed file concurrently use last-writer-wins semantics.
|
|
57
|
+
|
|
58
|
+
### Inspect the active route
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
curl http://localhost:5030/api/explorer/health
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The response includes:
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"fileRouting": {
|
|
69
|
+
"entitiesPrefix": "neo-memorix-entities",
|
|
70
|
+
"eventsPrefix": "neo-memorix-events",
|
|
71
|
+
"memoryPrefix": "neo-agent-memorix-catalox",
|
|
72
|
+
"metadataPrefix": "neo-agent-memorix-catalox"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The mock server stores all state in JSON files under the `mocks/` directory. At
|
|
78
|
+
startup, every file is loaded into an in-memory store. Mutations happen in
|
|
79
|
+
memory and are debounced-flushed back to disk automatically.
|
|
80
|
+
|
|
81
|
+
## Directory structure
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
mocks/
|
|
85
|
+
metadata/ # schema, config, descriptors
|
|
86
|
+
agents.json
|
|
87
|
+
object-types/
|
|
88
|
+
assets.json
|
|
89
|
+
findings.json
|
|
90
|
+
memory.json
|
|
91
|
+
product.json
|
|
92
|
+
marketing.json
|
|
93
|
+
admin.json
|
|
94
|
+
users.json
|
|
95
|
+
lists/
|
|
96
|
+
assets-default.json
|
|
97
|
+
findings-default.json
|
|
98
|
+
my-plate.json
|
|
99
|
+
this-week.json
|
|
100
|
+
backlog.json
|
|
101
|
+
narratives/
|
|
102
|
+
assets.json
|
|
103
|
+
findings.json
|
|
104
|
+
product.json
|
|
105
|
+
marketing.json
|
|
106
|
+
admin.json
|
|
107
|
+
write-descriptors/
|
|
108
|
+
assets-analysis-write.json
|
|
109
|
+
memory-write.json
|
|
110
|
+
product-task-write.json
|
|
111
|
+
marketing-task-write.json
|
|
112
|
+
admin-task-write.json
|
|
113
|
+
data/ # record collections
|
|
114
|
+
assets/
|
|
115
|
+
snapshots.json
|
|
116
|
+
analysis.json
|
|
117
|
+
decisions.json
|
|
118
|
+
events.json
|
|
119
|
+
findings/
|
|
120
|
+
snapshots.json
|
|
121
|
+
analysis.json
|
|
122
|
+
decisions.json
|
|
123
|
+
events.json
|
|
124
|
+
product/
|
|
125
|
+
snapshots.json
|
|
126
|
+
marketing/
|
|
127
|
+
snapshots.json
|
|
128
|
+
admin/
|
|
129
|
+
snapshots.json
|
|
130
|
+
users/
|
|
131
|
+
snapshots.json
|
|
132
|
+
memory/
|
|
133
|
+
memory.json
|
|
134
|
+
system/
|
|
135
|
+
inventory.json
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Metadata files
|
|
139
|
+
|
|
140
|
+
Metadata files define the shape of the system: what object types exist, how
|
|
141
|
+
lists are queried, what narratives are authored, and how writes are validated.
|
|
142
|
+
|
|
143
|
+
### agents.json
|
|
144
|
+
|
|
145
|
+
Registry of agents that participate in the pipeline.
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"agents": [
|
|
150
|
+
{ "id": "agent-1", "name": "Discovery Agent", "role": "ingestion" },
|
|
151
|
+
{ "id": "agent-2", "name": "Analysis Agent", "role": "enrichment" },
|
|
152
|
+
{ "id": "agent-3", "name": "Decision Agent", "role": "remediation" }
|
|
153
|
+
]
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### object-types/\*.json
|
|
158
|
+
|
|
159
|
+
Each file defines one object type. The `catalogRelations` array declares
|
|
160
|
+
relationships between objects. The `rootPropertyCatalog` is computed at runtime
|
|
161
|
+
by the `POST .../root-property-catalog/compute` endpoint.
|
|
162
|
+
|
|
163
|
+
```json
|
|
164
|
+
{
|
|
165
|
+
"name": "assets",
|
|
166
|
+
"summary": "Infrastructure assets discovered and analyzed by Memorix.",
|
|
167
|
+
"catalogRelations": [
|
|
168
|
+
{
|
|
169
|
+
"sourceProperty": "linkedAssetId",
|
|
170
|
+
"targetObject": "assets",
|
|
171
|
+
"targetProperty": "recordId",
|
|
172
|
+
"relationType": "self-link"
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"sourceProperty": "ownerId",
|
|
176
|
+
"targetObject": "agents",
|
|
177
|
+
"targetProperty": "id",
|
|
178
|
+
"relationType": "ownership"
|
|
179
|
+
}
|
|
180
|
+
],
|
|
181
|
+
"rootPropertyCatalog": []
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
To add a new object type: create a new `.json` file under
|
|
186
|
+
`mocks/metadata/object-types/` and add a matching directory under
|
|
187
|
+
`mocks/data/` with the content type files (at minimum `snapshots.json`).
|
|
188
|
+
|
|
189
|
+
### lists/\*.json
|
|
190
|
+
|
|
191
|
+
List descriptors define saved queries: which entity they target, which fields
|
|
192
|
+
to project, what extensions to join, and default sort order.
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
{
|
|
196
|
+
"id": "assets-default",
|
|
197
|
+
"entity": "assets",
|
|
198
|
+
"entityName": "assets",
|
|
199
|
+
"baseContentType": "snapshots",
|
|
200
|
+
"fields": ["recordId", "entityId", "severity", "status"],
|
|
201
|
+
"extensions": ["analysis", "decisions"],
|
|
202
|
+
"sort": { "severity": -1, "entityId": 1 },
|
|
203
|
+
"query": {},
|
|
204
|
+
"listId": "assets-default"
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
- `fields` controls which properties appear in the projected result.
|
|
209
|
+
- `extensions` names additional content types to join (e.g. `analysis`,
|
|
210
|
+
`decisions`) via the snapshot alias pipeline.
|
|
211
|
+
- `sort` is a mingo-compatible sort document (`1` = asc, `-1` = desc).
|
|
212
|
+
|
|
213
|
+
### narratives/\*.json
|
|
214
|
+
|
|
215
|
+
Authored narratives are key-value maps. Each key becomes a virtual tag that
|
|
216
|
+
records can carry.
|
|
217
|
+
|
|
218
|
+
```json
|
|
219
|
+
{
|
|
220
|
+
"has-vulns": {
|
|
221
|
+
"key": "has-vulns",
|
|
222
|
+
"label": "Has Vulnerabilities",
|
|
223
|
+
"description": "Asset carries at least one known vulnerability."
|
|
224
|
+
},
|
|
225
|
+
"exposed-public": {
|
|
226
|
+
"key": "exposed-public",
|
|
227
|
+
"label": "Exposed Publicly",
|
|
228
|
+
"description": "Asset is reachable from the public internet."
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Records reference narratives by setting `narratives` on the document:
|
|
234
|
+
|
|
235
|
+
```json
|
|
236
|
+
{
|
|
237
|
+
"recordId": "asset-1",
|
|
238
|
+
"narratives": { "has-vulns": true, "exposed-public": true }
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### write-descriptors/\*.json
|
|
243
|
+
|
|
244
|
+
Write descriptors define the JSON Schema for write operations and the target
|
|
245
|
+
collection.
|
|
246
|
+
|
|
247
|
+
```json
|
|
248
|
+
{
|
|
249
|
+
"writeDescriptorId": "assets-analysis-write",
|
|
250
|
+
"targetCollection": "assets/analysis",
|
|
251
|
+
"schema": {
|
|
252
|
+
"type": "object",
|
|
253
|
+
"required": ["recordId"],
|
|
254
|
+
"properties": {
|
|
255
|
+
"recordId": { "type": "string" },
|
|
256
|
+
"analysisType": { "type": "string" },
|
|
257
|
+
"score": { "type": "number" },
|
|
258
|
+
"notes": { "type": "array" }
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
- `targetCollection` is an `objectType/contentType` pair (e.g. `assets/analysis`).
|
|
265
|
+
- `schema` follows a subset of JSON Schema: `required` array and `properties`
|
|
266
|
+
map with `type` constraints. The write engine validates input against this
|
|
267
|
+
before executing the operation.
|
|
268
|
+
- **Required semantics:** `required` is enforced fully only for `add` /
|
|
269
|
+
`replace`. For `upsert` / `patch` / `delete`, only the identity field
|
|
270
|
+
(`recordId`, or `memoryId` on the memory tier) is mandatory. This lets the
|
|
271
|
+
UI send partial updates like `{recordId, status}` without re-supplying every
|
|
272
|
+
required field.
|
|
273
|
+
|
|
274
|
+
The demo app's task descriptors (`product-task-write.json`,
|
|
275
|
+
`marketing-task-write.json`, `admin-task-write.json`) all use this pattern.
|
|
276
|
+
See [demo-app.md](./demo-app.md) for the full walkthrough.
|
|
277
|
+
|
|
278
|
+
## Data files
|
|
279
|
+
|
|
280
|
+
Data files hold the actual record collections. Each object type gets its own
|
|
281
|
+
directory, and within it, one file per content type.
|
|
282
|
+
|
|
283
|
+
### Content types
|
|
284
|
+
|
|
285
|
+
| Content type | File | Identity key | Purpose |
|
|
286
|
+
| ------------- | ------------------- | -------------- | ------------------------------ |
|
|
287
|
+
| `snapshots` | `snapshots.json` | `recordId` | Entity snapshots (primary tier)|
|
|
288
|
+
| `analysis` | `analysis.json` | `recordId` | Knowledge/analysis records |
|
|
289
|
+
| `decisions` | `decisions.json` | `recordId` | Decision records |
|
|
290
|
+
| `events` | `events.json` | `eventId` | Event records |
|
|
291
|
+
| `memory` | `memory.json` | `memoryId` | Fourth tier (standalone) |
|
|
292
|
+
|
|
293
|
+
### Record structure
|
|
294
|
+
|
|
295
|
+
Records are plain JSON objects. Every record must carry exactly one identity
|
|
296
|
+
key. Records can include narrative tags and associated data buckets.
|
|
297
|
+
|
|
298
|
+
```json
|
|
299
|
+
[
|
|
300
|
+
{
|
|
301
|
+
"recordId": "asset-1",
|
|
302
|
+
"entityId": "web-prod-01",
|
|
303
|
+
"severity": "critical",
|
|
304
|
+
"status": "open",
|
|
305
|
+
"region": "us-east-1",
|
|
306
|
+
"ownerId": "agent-1",
|
|
307
|
+
"narratives": { "has-vulns": true, "exposed-public": true },
|
|
308
|
+
"associatedData": [
|
|
309
|
+
{ "kind": "tag", "value": "prod" },
|
|
310
|
+
{ "kind": "tag", "value": "web" }
|
|
311
|
+
],
|
|
312
|
+
"associatedInferred": [
|
|
313
|
+
{ "kind": "discovery", "value": "public-ip" }
|
|
314
|
+
],
|
|
315
|
+
"associatedAnalysis": [
|
|
316
|
+
{ "kind": "analysis", "value": "cve-score-9" }
|
|
317
|
+
]
|
|
318
|
+
}
|
|
319
|
+
]
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Associated data buckets
|
|
323
|
+
|
|
324
|
+
Records carry associated data in prefixed fields. The API translates these
|
|
325
|
+
between storage format and the normalized `associated` bucket returned to
|
|
326
|
+
clients.
|
|
327
|
+
|
|
328
|
+
| Storage field | Client alias | Description |
|
|
329
|
+
| ----------------------- | --------------- | ---------------------- |
|
|
330
|
+
| `associatedData` | `data` | Explicit tags |
|
|
331
|
+
| `associatedDiscovery` | `discovery` | Discovery-phase data |
|
|
332
|
+
| `associatedInferred` | `discovery` | Inferred discovery |
|
|
333
|
+
| `associatedAnalysis` | `analysis` | Analysis-phase data |
|
|
334
|
+
| `associated{Custom}` | `{custom}` | Custom bucket |
|
|
335
|
+
|
|
336
|
+
### Memory tier
|
|
337
|
+
|
|
338
|
+
Memory is the fourth data tier. It lives under `mocks/data/memory/memory.json`
|
|
339
|
+
and uses `memoryId` as its identity key (instead of `recordId`).
|
|
340
|
+
|
|
341
|
+
```json
|
|
342
|
+
[
|
|
343
|
+
{
|
|
344
|
+
"memoryId": "mem-1",
|
|
345
|
+
"entityId": "web-prod-01",
|
|
346
|
+
"kind": "conversation-summary",
|
|
347
|
+
"summary": "Discussed upgrade path for web-prod-01 from v2 to v3.",
|
|
348
|
+
"createdAt": "2026-07-10T08:00:00Z"
|
|
349
|
+
}
|
|
350
|
+
]
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### System inventory
|
|
354
|
+
|
|
355
|
+
`mocks/data/system/inventory.json` is a special file used by the inventory
|
|
356
|
+
engine for policy configuration.
|
|
357
|
+
|
|
358
|
+
## How the store works
|
|
359
|
+
|
|
360
|
+
1. On startup, `InMemoryStore.load()` reads all metadata and data JSON files
|
|
361
|
+
into JavaScript `Map` objects.
|
|
362
|
+
2. All API reads serve from the in-memory maps (no disk I/O per request).
|
|
363
|
+
3. Writes mutate the in-memory maps and mark files as "dirty".
|
|
364
|
+
4. Dirty files are flushed to disk after a debounce (default 300ms, configured
|
|
365
|
+
via `DISK_FLUSH_DEBOUNCE_MS`).
|
|
366
|
+
5. On `SIGINT`/`SIGTERM`, all dirty files are flushed immediately before
|
|
367
|
+
shutdown.
|
|
368
|
+
|
|
369
|
+
You can modify JSON files on disk while the server is running, but changes
|
|
370
|
+
won't be picked up until the next restart (the store loads once at boot). If
|
|
371
|
+
you need to persist changes made via the API, they are written back
|
|
372
|
+
automatically.
|
|
373
|
+
|
|
374
|
+
## Adding a new object type
|
|
375
|
+
|
|
376
|
+
1. Create `mocks/metadata/object-types/<name>.json` following the schema above.
|
|
377
|
+
2. Create `mocks/data/<name>/` directory.
|
|
378
|
+
3. Add at least `mocks/data/<name>/snapshots.json` with an empty array `[]`.
|
|
379
|
+
4. Optionally add `analysis.json`, `decisions.json`, `events.json`.
|
|
380
|
+
5. Create `mocks/metadata/lists/<name>-default.json` for a default list view.
|
|
381
|
+
6. Create `mocks/metadata/narratives/<name>.json` if the object type has
|
|
382
|
+
authored narratives.
|
|
383
|
+
7. Restart the server. The new object type appears in
|
|
384
|
+
`GET /api/explorer/object-types` and the inventory.
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
# Running the Service
|
|
2
|
+
|
|
3
|
+
The mock server runs as a CLI tool or as a development server. It listens for
|
|
4
|
+
HTTP requests on a configurable port and serves the full Memorix Explorer API
|
|
5
|
+
from in-memory state backed by JSON files on disk.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
### From npm (global)
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g @x12i/static-memorix
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
This makes the `static-memorix` command available globally.
|
|
16
|
+
`memorix-explorer` remains available as a short, friendly alias.
|
|
17
|
+
|
|
18
|
+
### From source (git clone)
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
git clone https://github.com/x12i/static-memorix.git
|
|
22
|
+
cd static-memorix
|
|
23
|
+
npm install
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Starting the server
|
|
27
|
+
|
|
28
|
+
### Production mode (build then run)
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm run build
|
|
32
|
+
npm start
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Development mode (no build step)
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm run dev
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Uses Node's `--experimental-strip-types` to run TypeScript directly. Faster
|
|
42
|
+
iteration, but not suitable for distribution.
|
|
43
|
+
|
|
44
|
+
### As a global CLI
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
static-memorix
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
After `npm install -g`, the CLI starts the server with default settings. The
|
|
51
|
+
mocks directory defaults to the bundled fixtures, and the CLI prints the URLs
|
|
52
|
+
to open:
|
|
53
|
+
|
|
54
|
+
```text
|
|
55
|
+
Memorix Explorer is ready
|
|
56
|
+
Demo: http://localhost:5030/demo
|
|
57
|
+
API: http://localhost:5030/api/explorer
|
|
58
|
+
Health: http://localhost:5030/health
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Create a writable demo
|
|
62
|
+
|
|
63
|
+
The bundled fixtures may live inside a global npm installation. Create a
|
|
64
|
+
user-owned copy before experimenting with create/edit/delete:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
static-memorix create-demo
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The command creates `./memorix-demo` and prints the exact next step:
|
|
71
|
+
|
|
72
|
+
```text
|
|
73
|
+
Demo created:
|
|
74
|
+
./memorix-demo
|
|
75
|
+
|
|
76
|
+
Start the server with:
|
|
77
|
+
static-memorix --mocks-dir "./memorix-demo"
|
|
78
|
+
|
|
79
|
+
Then open:
|
|
80
|
+
http://localhost:5030/demo
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The generated directory contains `demo.html`, `data/`, `metadata/`, and a
|
|
84
|
+
README. It is safe to edit and suitable for source control. The command refuses
|
|
85
|
+
to overwrite an existing file or directory.
|
|
86
|
+
|
|
87
|
+
Use a custom folder or port:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
static-memorix create-demo --dir ./fixtures
|
|
91
|
+
static-memorix --mocks-dir ./fixtures --port 8080
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Stop the server
|
|
95
|
+
|
|
96
|
+
Press Ctrl+C in the terminal where the server is running, or stop a managed
|
|
97
|
+
instance from another terminal:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
static-memorix stop
|
|
101
|
+
static-memorix stop --port 8080
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The CLI tracks only server processes it started and refuses to terminate a PID
|
|
105
|
+
that does not identify as Memorix Explorer. Stale tracking files are removed
|
|
106
|
+
automatically. If startup finds an occupied port, the error prints the matching
|
|
107
|
+
stop command and suggests the next port.
|
|
108
|
+
|
|
109
|
+
### With `npx` (no global install)
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npx @x12i/static-memorix
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Choose a port with a CLI option or environment variable. CLI options take
|
|
116
|
+
precedence:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
static-memorix --port 5030 --host 127.0.0.1
|
|
120
|
+
PORT=5030 HOST=127.0.0.1 static-memorix
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Built-in help and version output do not start the server:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
static-memorix --help
|
|
127
|
+
static-memorix --version
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### From application code
|
|
131
|
+
|
|
132
|
+
Importing the package does not open a port. Call `startServer()` explicitly:
|
|
133
|
+
|
|
134
|
+
```js
|
|
135
|
+
import { startServer } from "@x12i/static-memorix";
|
|
136
|
+
|
|
137
|
+
const app = await startServer({
|
|
138
|
+
port: 5030,
|
|
139
|
+
host: "127.0.0.1",
|
|
140
|
+
installSignalHandlers: false,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Later, for example during test teardown:
|
|
144
|
+
await app.close();
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
For tests that do not need a real TCP listener, build the Fastify app and use
|
|
148
|
+
its injection API:
|
|
149
|
+
|
|
150
|
+
```js
|
|
151
|
+
import { buildServer } from "@x12i/static-memorix";
|
|
152
|
+
|
|
153
|
+
const app = await buildServer();
|
|
154
|
+
const response = await app.inject({ method: "GET", url: "/health" });
|
|
155
|
+
console.log(response.json()); // { ok: true }
|
|
156
|
+
await app.close();
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Configuration
|
|
160
|
+
|
|
161
|
+
All configuration is via environment variables. Defaults work out of the box.
|
|
162
|
+
|
|
163
|
+
### Core
|
|
164
|
+
|
|
165
|
+
| Variable | Default | Description |
|
|
166
|
+
| --------- | ----------- | ------------------------------ |
|
|
167
|
+
| `PORT` | `5030` | HTTP listen port |
|
|
168
|
+
| `HOST` | `0.0.0.0` | HTTP listen host |
|
|
169
|
+
| `MOCKS_DIR` | bundled `<package>/mocks` | Root directory for JSON fixtures |
|
|
170
|
+
|
|
171
|
+
`MOCKS_DIR` must be writable if you use mutation endpoints. For installed or
|
|
172
|
+
containerized deployments, point it at a persistent application-owned volume
|
|
173
|
+
rather than relying on the package's bundled seed directory:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
MOCKS_DIR=/var/lib/my-app/memorix-mocks static-memorix --port 5030
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Configuration is resolved when the package is imported. In programmatic usage,
|
|
180
|
+
set `process.env.MOCKS_DIR`, `MEMORIX_ORG_ID`, and `MEMORIX_AGENT_ID` before the
|
|
181
|
+
first dynamic import, and run separate processes for separate routing contexts.
|
|
182
|
+
|
|
183
|
+
### DB Router Simulation
|
|
184
|
+
|
|
185
|
+
These variables control the fake database routing strings returned by the
|
|
186
|
+
health endpoint. The frontend uses these to verify it is connected to the
|
|
187
|
+
correct tenant.
|
|
188
|
+
|
|
189
|
+
| Variable | Default | Description |
|
|
190
|
+
| --------------------------- | ----------------- | ---------------------------------------- |
|
|
191
|
+
| `MEMORIX_ORG_ID` | `memorix` | Organization prefix |
|
|
192
|
+
| `MEMORIX_AGENT_ID` | `default-agent` | Agent prefix for Catalox DB routing |
|
|
193
|
+
| `MEMORIX_DEPLOYMENT_PROFILE`| `default` | When `ebook`, forces org/agent to `ebooks`|
|
|
194
|
+
|
|
195
|
+
The health endpoint returns:
|
|
196
|
+
- `memorixDb`: `"<orgId>-memorix-entities + <orgId>-memorix-events"`
|
|
197
|
+
- `cataloxDb`: `"<agentId>-memorix-catalox"`
|
|
198
|
+
- `fileRouting`: the concrete entity, event, memory, and metadata filename
|
|
199
|
+
prefixes selected for this process
|
|
200
|
+
|
|
201
|
+
These are functional routes, not display-only labels. For example:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
MEMORIX_ORG_ID=neo MEMORIX_AGENT_ID=neo-agent npm start
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
selects filenames beginning with `neo-memorix-entities--`,
|
|
208
|
+
`neo-memorix-events--`, and `neo-agent-memorix-catalox--`. See
|
|
209
|
+
[managing-json-files.md](./managing-json-files.md#file-backed-database-routing)
|
|
210
|
+
for exact examples and fallback behavior.
|
|
211
|
+
|
|
212
|
+
### Feature flags
|
|
213
|
+
|
|
214
|
+
Metadata mutations on lists and narratives are gated behind feature flags.
|
|
215
|
+
Record mutations are governed by write descriptors and are available by
|
|
216
|
+
default.
|
|
217
|
+
|
|
218
|
+
| Variable | Controls |
|
|
219
|
+
| ------------------------------------------- | --------------------- |
|
|
220
|
+
| `MEMORIX_EXPLORER_ENABLE_METADATA_WRITES` | List & narrative CRUD |
|
|
221
|
+
| `MEMORIX_EXPLORER_ENABLE_PIPELINE_WRITES` | Pipeline writes |
|
|
222
|
+
| `MEMORIX_EXPLORER_ENABLE_REGISTRY_WRITES` | Registry writes |
|
|
223
|
+
|
|
224
|
+
Set to `true` or `1` to enable:
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
MEMORIX_EXPLORER_ENABLE_METADATA_WRITES=true static-memorix
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Disk flush
|
|
231
|
+
|
|
232
|
+
| Variable | Default | Description |
|
|
233
|
+
| -------------------------- | ------- | --------------------------------------- |
|
|
234
|
+
| `DISK_FLUSH_DEBOUNCE_MS` | `300` | Milliseconds to debounce before writing dirty files to disk |
|
|
235
|
+
|
|
236
|
+
### Example: full custom configuration
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
PORT=8080 \
|
|
240
|
+
HOST=127.0.0.1 \
|
|
241
|
+
MOCKS_DIR=/path/to/my/fixtures \
|
|
242
|
+
MEMORIX_ORG_ID=neo \
|
|
243
|
+
MEMORIX_AGENT_ID=neo-agent \
|
|
244
|
+
MEMORIX_EXPLORER_ENABLE_METADATA_WRITES=true \
|
|
245
|
+
static-memorix
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Health checks
|
|
249
|
+
|
|
250
|
+
Once running, verify the server is up:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
curl localhost:5030/health
|
|
254
|
+
# {"ok":true}
|
|
255
|
+
|
|
256
|
+
curl "localhost:5030/api/explorer/health?includeInventory=1"
|
|
257
|
+
# {"ok":true,"mongoUriConfigured":true,"discoverySample":["assets","findings","memory"],...}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## Graceful shutdown
|
|
261
|
+
|
|
262
|
+
The server handles `SIGINT` and `SIGTERM`. On shutdown it:
|
|
263
|
+
1. Flushes all dirty in-memory state to disk.
|
|
264
|
+
2. Closes the Fastify listener.
|
|
265
|
+
3. Exits with code 0.
|
|
266
|
+
|
|
267
|
+
This means mutations made via the API are always persisted as long as the
|
|
268
|
+
server shuts down cleanly. If the process is killed with `SIGKILL`, unsaved
|
|
269
|
+
state is lost.
|