total-recall-brain 3.23.3 → 3.24.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/bin/total-recall.mjs +1 -0
- package/frontend/dist/assets/{index-c6GHGS_j.js → index-CtCDaZYZ.js} +1 -1
- package/frontend/dist/index.html +1 -1
- package/frontend/src/pages/SecretsPage.tsx +111 -52
- package/package.json +3 -2
- package/scaffold/.agent/skills/total-recall/SKILL.md +199 -7
- package/scaffold/.agent/skills/total-recall/memory-vault/concepts/cli-help-reference.md +13 -4
- package/scaffold/.agent/skills/total-recall/memory-vault/invariants/operating-instructions.md +2 -0
- package/scaffold/.agent/skills/total-recall/references/api-reference.md +1 -1
- package/scaffold/.agent/skills/total-recall/references/cli-reference.md +1 -2
- package/scaffold/.agent/skills/total-recall/research-queue.jsonl +7 -0
- package/scaffold/.agent/skills/total-recall/skills-registry/index.yaml +2461 -6
- package/src/cli/backfill.mjs +176 -0
- package/src/cli/init.mjs +1 -1
- package/src/cli/proposals.mjs +3 -3
- package/src/cli/secret.mjs +84 -0
- package/src/core/clarity-rewriter.mjs +25 -39
- package/src/core/conclusion-writer.mjs +20 -10
- package/src/core/crypto.mjs +86 -16
- package/src/core/daemon-loop.mjs +2 -2
- package/src/core/dream.mjs +49 -58
- package/src/core/env-import.mjs +18 -0
- package/src/core/inference-engine.mjs +15 -17
- package/src/core/migrate.mjs +5 -1
- package/src/core/okf-adapter.mjs +2 -0
- package/src/core/post-mortem.mjs +30 -26
- package/src/core/proposal-applier.mjs +26 -10
- package/src/core/repo-sync.mjs +51 -39
- package/src/core/scheduler.mjs +1 -0
- package/src/core/schema.mjs +24 -1
- package/src/core/search.mjs +1 -1
- package/src/core/secrets-env-diff.mjs +132 -0
- package/src/core/secrets-rekey.mjs +0 -0
- package/src/core/snapshot.mjs +32 -9
- package/src/core/ssss-kernel-bridge.mjs +46 -6
- package/src/core/task-envelope.mjs +1 -0
- package/src/core/validated-write.mjs +68 -5
- package/src/core/vault-backfill.mjs +259 -0
- package/src/core/vault.mjs +22 -4
- package/src/server/auth.mjs +42 -8
- package/src/server/routes/brains.mjs +1 -1
- package/src/server/routes/proposals.mjs +4 -4
- package/src/server/routes/secrets.mjs +1 -1
package/frontend/dist/index.html
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
18
18
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
|
|
19
19
|
<title>Total Recall — Portable Memory</title>
|
|
20
|
-
<script type="module" crossorigin src="/assets/index-
|
|
20
|
+
<script type="module" crossorigin src="/assets/index-CtCDaZYZ.js"></script>
|
|
21
21
|
<link rel="stylesheet" crossorigin href="/assets/index-C7_Xpk27.css">
|
|
22
22
|
</head>
|
|
23
23
|
<body>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useEffect, useCallback, useMemo, useRef, type CSSProperties, type ReactNode } from 'react'
|
|
1
|
+
import { useState, useEffect, useCallback, useMemo, useRef, memo, type CSSProperties, type ReactNode } from 'react'
|
|
2
2
|
import {
|
|
3
3
|
listApiKeys,
|
|
4
4
|
issueApiKey,
|
|
@@ -294,36 +294,36 @@ export default function SecretsPage() {
|
|
|
294
294
|
}
|
|
295
295
|
}
|
|
296
296
|
|
|
297
|
+
// The loaders below set state synchronously before their first await, which
|
|
298
|
+
// react-hooks/set-state-in-effect rightly rejects inside an effect body. The
|
|
299
|
+
// original code dodged it with setTimeout(..., 0); yielding a microtask does
|
|
300
|
+
// the same job a macrotask sooner, and unlike the timeout it can be cancelled
|
|
301
|
+
// — so a fast unmount no longer sets state on a dead component.
|
|
297
302
|
useEffect(() => {
|
|
298
|
-
if (tab
|
|
299
|
-
|
|
303
|
+
if (tab !== 'sync') return
|
|
304
|
+
let cancelled = false
|
|
305
|
+
void Promise.resolve().then(() => {
|
|
306
|
+
if (!cancelled) void loadSyncData()
|
|
307
|
+
})
|
|
308
|
+
return () => {
|
|
309
|
+
cancelled = true
|
|
300
310
|
}
|
|
301
311
|
}, [tab, loadSyncData])
|
|
302
312
|
|
|
313
|
+
/**
|
|
314
|
+
* Loads the secrets catalog — and ONLY the catalog.
|
|
315
|
+
*
|
|
316
|
+
* This used to await, in series: config, then the Gemini / Claude / OpenAI /
|
|
317
|
+
* OpenRouter model lists, and finally the catalog. The page's actual content
|
|
318
|
+
* therefore sat behind four external vendor round-trips, and the spinner ran
|
|
319
|
+
* until the slowest one answered. The model lists belong to the config tab
|
|
320
|
+
* and are fetched separately (and lazily) below.
|
|
321
|
+
*/
|
|
303
322
|
const loadCatalog = useCallback(async () => {
|
|
304
323
|
setLoading(true)
|
|
305
324
|
setError(null)
|
|
306
325
|
try {
|
|
307
|
-
|
|
308
|
-
try {
|
|
309
|
-
const config = await fetchConfigJson()
|
|
310
|
-
if (!config.secrets) config.secrets = {}
|
|
311
|
-
if (!config.brain) config.brain = {}
|
|
312
|
-
setConfigData(config)
|
|
313
|
-
|
|
314
|
-
const gem = await fetchGeminiModels().catch(() => [])
|
|
315
|
-
setGeminiModels(gem)
|
|
316
|
-
const cla = await fetchClaudeModels().catch(() => [])
|
|
317
|
-
setClaudeModels(cla)
|
|
318
|
-
const open = await fetchOpenaiModels().catch(() => [])
|
|
319
|
-
setOpenaiModels(open)
|
|
320
|
-
const or = await fetchOpenRouterModels().catch(() => [])
|
|
321
|
-
setOrModels(or)
|
|
322
|
-
} catch {
|
|
323
|
-
// ignore fetching models error
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
const data = await fetchSecretsCatalog()
|
|
326
|
+
const data = await fetchSecretsCatalog()
|
|
327
327
|
setCatalog(data)
|
|
328
328
|
const skey = selectedKeyRef.current
|
|
329
329
|
if (skey) {
|
|
@@ -342,6 +342,35 @@ export default function SecretsPage() {
|
|
|
342
342
|
}
|
|
343
343
|
}, [])
|
|
344
344
|
|
|
345
|
+
const loadConfig = useCallback(async () => {
|
|
346
|
+
try {
|
|
347
|
+
const config = await fetchConfigJson()
|
|
348
|
+
if (!config.secrets) config.secrets = {}
|
|
349
|
+
if (!config.brain) config.brain = {}
|
|
350
|
+
setConfigData(config)
|
|
351
|
+
} catch {
|
|
352
|
+
// config is only needed by the config tab; its absence must not blank the page
|
|
353
|
+
}
|
|
354
|
+
}, [])
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Vendor model lists, concurrently rather than in series. Each already
|
|
358
|
+
* degrades to [] on its own, so one slow or unauthorized vendor cannot hold
|
|
359
|
+
* up the other three.
|
|
360
|
+
*/
|
|
361
|
+
const loadModels = useCallback(async () => {
|
|
362
|
+
const [gem, cla, open, or] = await Promise.all([
|
|
363
|
+
fetchGeminiModels().catch(() => []),
|
|
364
|
+
fetchClaudeModels().catch(() => []),
|
|
365
|
+
fetchOpenaiModels().catch(() => []),
|
|
366
|
+
fetchOpenRouterModels().catch(() => []),
|
|
367
|
+
])
|
|
368
|
+
setGeminiModels(gem)
|
|
369
|
+
setClaudeModels(cla)
|
|
370
|
+
setOpenaiModels(open)
|
|
371
|
+
setOrModels(or)
|
|
372
|
+
}, [])
|
|
373
|
+
|
|
345
374
|
const loadPats = useCallback(async () => {
|
|
346
375
|
try {
|
|
347
376
|
const r = await listApiKeys()
|
|
@@ -360,12 +389,27 @@ export default function SecretsPage() {
|
|
|
360
389
|
}, [])
|
|
361
390
|
|
|
362
391
|
useEffect(() => {
|
|
363
|
-
|
|
392
|
+
let cancelled = false
|
|
393
|
+
void Promise.resolve().then(() => {
|
|
394
|
+
if (cancelled) return
|
|
364
395
|
void loadCatalog()
|
|
365
396
|
void loadPats()
|
|
366
397
|
void loadWebAuthn()
|
|
367
|
-
|
|
368
|
-
|
|
398
|
+
void loadConfig()
|
|
399
|
+
})
|
|
400
|
+
return () => {
|
|
401
|
+
cancelled = true
|
|
402
|
+
}
|
|
403
|
+
}, [loadCatalog, loadPats, loadWebAuthn, loadConfig])
|
|
404
|
+
|
|
405
|
+
// Vendor model lists are only rendered by the 'cloud' tab — don't pay for
|
|
406
|
+
// them until it is opened, and only pay once.
|
|
407
|
+
const modelsLoadedRef = useRef(false)
|
|
408
|
+
useEffect(() => {
|
|
409
|
+
if (tab !== 'cloud' || modelsLoadedRef.current) return
|
|
410
|
+
modelsLoadedRef.current = true
|
|
411
|
+
void loadModels()
|
|
412
|
+
}, [tab, loadModels])
|
|
369
413
|
|
|
370
414
|
|
|
371
415
|
const updateSecretsProp = (prop: string, value: string) => {
|
|
@@ -402,7 +446,11 @@ export default function SecretsPage() {
|
|
|
402
446
|
|
|
403
447
|
// metaForm hoisted
|
|
404
448
|
|
|
405
|
-
|
|
449
|
+
// useCallback with an empty dep list: every setter below is a stable React
|
|
450
|
+
// setter and the ref is stable, so this identity never needs to change. That
|
|
451
|
+
// is what lets the memoized SecretKeyRow skip re-rendering — a fresh arrow
|
|
452
|
+
// function per row per render would defeat memo() entirely.
|
|
453
|
+
const openKey = useCallback((k: SecretCatalogKey, sectionId?: string) => {
|
|
406
454
|
selectedKeyRef.current = k.key
|
|
407
455
|
setSelected(k)
|
|
408
456
|
setEdit(metaForm(k))
|
|
@@ -418,7 +466,7 @@ export default function SecretsPage() {
|
|
|
418
466
|
return next
|
|
419
467
|
})
|
|
420
468
|
}
|
|
421
|
-
}
|
|
469
|
+
}, [])
|
|
422
470
|
|
|
423
471
|
async function registerPasskey() {
|
|
424
472
|
if (!browserSupportsWebAuthn()) {
|
|
@@ -623,30 +671,33 @@ export default function SecretsPage() {
|
|
|
623
671
|
}
|
|
624
672
|
}
|
|
625
673
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
674
|
+
/**
|
|
675
|
+
* Filtered key list.
|
|
676
|
+
*
|
|
677
|
+
* There were two copies of this predicate — this one, recomputed on EVERY
|
|
678
|
+
* render, and a second inside the repoSections memo. With ~35 pieces of state
|
|
679
|
+
* on this component, "every render" means every keystroke anywhere, every
|
|
680
|
+
* save, every reveal. One memoized predicate now feeds both consumers, so
|
|
681
|
+
* they can also never disagree about what "matching" means.
|
|
682
|
+
*/
|
|
683
|
+
const keys = useMemo(() => {
|
|
684
|
+
const all = catalog?.keys || []
|
|
685
|
+
const q = filter.trim().toLowerCase()
|
|
686
|
+
if (!q) return all
|
|
687
|
+
return all.filter(
|
|
688
|
+
(k) =>
|
|
689
|
+
k.key.toLowerCase().includes(q) ||
|
|
690
|
+
(k.provider || '').toLowerCase().includes(q) ||
|
|
691
|
+
(k.repos || []).some((r) => r.toLowerCase().includes(q)) ||
|
|
692
|
+
(k.label || '').toLowerCase().includes(q)
|
|
634
693
|
)
|
|
635
|
-
})
|
|
694
|
+
}, [catalog, filter])
|
|
636
695
|
|
|
637
696
|
/** Group filtered keys into repo sections (one key → one repo; multi = error; none = developer). */
|
|
638
697
|
const repoSections = useMemo<{ id: string; label: string; kind: 'error' | 'developer' | 'repo' | 'provider'; keys: SecretCatalogKey[] }[]>(() => {
|
|
639
698
|
if (!catalog?.keys) return []
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
(k) =>
|
|
643
|
-
k.key.toLowerCase().includes(f) ||
|
|
644
|
-
(k.provider || '').toLowerCase().includes(f) ||
|
|
645
|
-
(k.repos || []).some((r) => r.toLowerCase().includes(f)) ||
|
|
646
|
-
(k.label || '').toLowerCase().includes(f)
|
|
647
|
-
)
|
|
648
|
-
return groupBy === 'api' ? groupKeysByProvider(filtered) : groupKeysByRepo(filtered)
|
|
649
|
-
}, [catalog, filter, groupBy])
|
|
699
|
+
return groupBy === 'api' ? groupKeysByProvider(keys) : groupKeysByRepo(keys)
|
|
700
|
+
}, [catalog, keys, groupBy])
|
|
650
701
|
|
|
651
702
|
// Default accordion: open errors + first product/developer section only
|
|
652
703
|
useEffect(() => {
|
|
@@ -1324,8 +1375,9 @@ export default function SecretsPage() {
|
|
|
1324
1375
|
<SecretKeyRow
|
|
1325
1376
|
key={`${sec.id}::${k.key}`}
|
|
1326
1377
|
k={k}
|
|
1378
|
+
sectionId={sec.id}
|
|
1327
1379
|
selected={selected?.key === k.key}
|
|
1328
|
-
onOpen={
|
|
1380
|
+
onOpen={openKey}
|
|
1329
1381
|
groupBy={groupBy}
|
|
1330
1382
|
/>
|
|
1331
1383
|
))}
|
|
@@ -2565,21 +2617,28 @@ function groupKeysByRepo(keys: SecretCatalogKey[]): {
|
|
|
2565
2617
|
}
|
|
2566
2618
|
|
|
2567
2619
|
/** Compact single-line-ish row for long catalogs */
|
|
2568
|
-
|
|
2620
|
+
/**
|
|
2621
|
+
* memo()'d because the catalog renders ~100 of these and the list re-renders
|
|
2622
|
+
* on every keystroke in the filter box. Paired with the stable `onOpen`
|
|
2623
|
+
* above, an unrelated state change now re-renders zero rows.
|
|
2624
|
+
*/
|
|
2625
|
+
const SecretKeyRow = memo(function SecretKeyRow({
|
|
2569
2626
|
k,
|
|
2570
2627
|
selected,
|
|
2628
|
+
sectionId,
|
|
2571
2629
|
onOpen,
|
|
2572
2630
|
groupBy,
|
|
2573
2631
|
}: {
|
|
2574
2632
|
k: SecretCatalogKey
|
|
2575
2633
|
selected: boolean
|
|
2576
|
-
|
|
2634
|
+
sectionId?: string
|
|
2635
|
+
onOpen: (k: SecretCatalogKey, sectionId?: string) => void
|
|
2577
2636
|
groupBy?: 'repo' | 'api'
|
|
2578
2637
|
}) {
|
|
2579
2638
|
return (
|
|
2580
2639
|
<button
|
|
2581
2640
|
type="button"
|
|
2582
|
-
onClick={onOpen}
|
|
2641
|
+
onClick={() => onOpen(k, sectionId)}
|
|
2583
2642
|
style={{
|
|
2584
2643
|
width: '100%',
|
|
2585
2644
|
display: 'grid',
|
|
@@ -2668,7 +2727,7 @@ function SecretKeyRow({
|
|
|
2668
2727
|
<span style={{ fontSize: 11, color: 'var(--text-tertiary)', flexShrink: 0 }}>›</span>
|
|
2669
2728
|
</button>
|
|
2670
2729
|
)
|
|
2671
|
-
}
|
|
2730
|
+
})
|
|
2672
2731
|
|
|
2673
2732
|
function SummaryCard({ label, value, accent }: { label: string; value: string; accent: string }) {
|
|
2674
2733
|
return (
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "total-recall-brain",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.24.0",
|
|
4
4
|
"description": "Personal AI system — database-free, SSSS-powered intelligence engine deployable to any POSIX host.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"workspaces": [
|
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
"routes:manifest": "node scripts/generate-route-manifest.mjs",
|
|
43
43
|
"clean": "rm -rf scratch/* .remember/* *.log",
|
|
44
44
|
"check:dist": "node scripts/check-dist-freshness.mjs",
|
|
45
|
-
"prepublishOnly": "npm run check:dist"
|
|
45
|
+
"prepublishOnly": "npm run check:dist",
|
|
46
|
+
"check:ssss-registry": "node scripts/check-ssss-registry.mjs"
|
|
46
47
|
},
|
|
47
48
|
"repository": {
|
|
48
49
|
"type": "git",
|
|
@@ -3,15 +3,16 @@ name: total-recall
|
|
|
3
3
|
provenance: total-recall
|
|
4
4
|
description: >-
|
|
5
5
|
Use this skill to operate Total Recall — portable memory, instructions,
|
|
6
|
-
openwiki,
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
openwiki, skills management, secrets store, mesh/headscale control server, and
|
|
7
|
+
the test suite. MANDATORY: Read this file before changing TR setup. Nested
|
|
8
|
+
packages under modules/ are NOT agent skills.
|
|
9
|
+
version: 3.23.3
|
|
9
10
|
repo_scoped: true
|
|
10
11
|
---
|
|
11
12
|
|
|
12
13
|
# Total Recall — Master Agent Skill
|
|
13
14
|
|
|
14
|
-
> **Product focus:** portable personal memory + IDE instructions + openwiki +
|
|
15
|
+
> **Product focus:** portable personal memory + IDE instructions + openwiki + skills management + secrets + mesh.
|
|
15
16
|
> **Only agent skill in this package.** Implementation helpers live in `modules/` (not skills).
|
|
16
17
|
> Openwiki ships with the brain at `openwiki/`.
|
|
17
18
|
|
|
@@ -60,6 +61,18 @@ You MUST actively reference and apply this skill under the following specific ru
|
|
|
60
61
|
* **Trigger**: Immediately after performing any memory node writes.
|
|
61
62
|
* **Action**: Rebuild the instruction shims. If using CLI commands (`npx total-recall remember` / `forget`), recompilation runs automatically in the background (asynchronously via detached subprocesses) to minimize latency. If editing vault files directly, trigger manual compilation by sending a POST request to `/api/vault/compile` or running `npx total-recall compile`.
|
|
62
63
|
|
|
64
|
+
### 5b. When handling ANY credential, API key, or password
|
|
65
|
+
Never invent a new place to keep it. It goes in the encrypted secrets store —
|
|
66
|
+
see **SECRETS STORE** below for `--stdin`, the one-repo binding rule, the
|
|
67
|
+
`.env` migration path, and the health gates.
|
|
68
|
+
|
|
69
|
+
### 5c. When reaching another machine, or editing device/node details
|
|
70
|
+
Use the **MESH / CONTROL SERVER** section. Node details are entity fields on
|
|
71
|
+
SSSS `mesh_node` documents — never hardcoded hostnames.
|
|
72
|
+
|
|
73
|
+
### 5d. When running tests, typechecks, or builds
|
|
74
|
+
See **TESTS**. Heavy runs go to the Mac Mini or the droplet, never the laptop.
|
|
75
|
+
|
|
63
76
|
### 6. When troubleshooting connections, port blocks, or sync errors
|
|
64
77
|
* **Trigger**: If the REST server is unreachable, ports are blocked, or the upstream sync tool encounters errors.
|
|
65
78
|
* **Action**: Refer to the active diagnostics manual within this skill to heal the local runtime.
|
|
@@ -106,6 +119,9 @@ node .agent/skills/total-recall/scripts/sync-repo.mjs
|
|
|
106
119
|
```
|
|
107
120
|
This utility fetches standard skill and invariant definitions from the upstream repository, merges them non-destructively preserving custom user nodes, and auto-recompiles the workspace.
|
|
108
121
|
|
|
122
|
+
Everything below is the CLI equivalent — see **SKILLS MANAGEMENT** for the full
|
|
123
|
+
registry/multi-repo reference.
|
|
124
|
+
|
|
109
125
|
### 5. Premium Browser Setup Wizard Onboarding
|
|
110
126
|
For first-time system installations, migrations, or editor reconnections, launch the visual onboarding setup wizard:
|
|
111
127
|
```bash
|
|
@@ -315,6 +331,45 @@ curl -H "Authorization: Bearer <YOUR_PAT_TOKEN>" \
|
|
|
315
331
|
|
|
316
332
|
---
|
|
317
333
|
|
|
334
|
+
## 🧩 SKILLS MANAGEMENT (`skill` subcommand)
|
|
335
|
+
|
|
336
|
+
Two distinct layers. Confusing them is the usual cause of "my edit disappeared".
|
|
337
|
+
|
|
338
|
+
| Layer | What it is | Commands |
|
|
339
|
+
|---|---|---|
|
|
340
|
+
| **Local brain** | Skills installed in one brain's `skills/` dir | `find` `install` `create` `edit` `scan` `list` `remove` `publish` |
|
|
341
|
+
| **Registry + install map** | The catalog and every repo copy deployed from it | `register` `registry` `deploy` `status` `track` `discover` `sync` `push` `pull` |
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
npx total-recall skill list # what this brain has
|
|
345
|
+
npx total-recall skill registry # catalog entries (skills-registry/index.yaml)
|
|
346
|
+
npx total-recall skill status # registry + install map + DRIFT summary
|
|
347
|
+
npx total-recall skill status <id> # drift for one skill
|
|
348
|
+
|
|
349
|
+
npx total-recall skill track /path/to/repo # start managing a repo's .agent/skills
|
|
350
|
+
npx total-recall skill discover # scan tracked repos for skills
|
|
351
|
+
npx total-recall skill sync # two-way reconcile across repos
|
|
352
|
+
npx total-recall skill push # one-way: catalog/source → all installs
|
|
353
|
+
npx total-recall skill pull # one-way: installs → source → fan-out
|
|
354
|
+
npx total-recall skill deploy <id> --repo <path> # copy into one repo
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
**Which direction to use.** `sync` picks a winner per skill (`--prefer
|
|
358
|
+
newest|registry|install`, default `newest`). When you have just edited the
|
|
359
|
+
canonical copy and want every repo to match it, use `push` — `sync` can pick a
|
|
360
|
+
repo copy instead if its mtime is newer, which silently reverts your edit.
|
|
361
|
+
`--dry-run` first, always.
|
|
362
|
+
|
|
363
|
+
**No hardcoded repo paths.** Repos enter the set only via `skill track`, the
|
|
364
|
+
project registry, `--repo`, `TR_SYNC_REPOS`, or cwd detection. Core must never
|
|
365
|
+
name a product repo (this is an open-source invariant, not a preference).
|
|
366
|
+
|
|
367
|
+
**`--include-core`** is off by default: the `total-recall` skill itself is not
|
|
368
|
+
fanned out unless you ask, so a repo-local edit cannot clobber the master copy
|
|
369
|
+
by accident.
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
318
373
|
## 🔐 SECRETS STORE (separate from the memory vault — `secret` subcommand)
|
|
319
374
|
|
|
320
375
|
API keys and credentials are **not** memory nodes. They live encrypted at
|
|
@@ -324,13 +379,88 @@ INSTRUCTIONS.md/skill surface (`secret check-surfaces` fails the build if one
|
|
|
324
379
|
ever leaks in). Full reference: `npx total-recall secret --help`.
|
|
325
380
|
|
|
326
381
|
```bash
|
|
327
|
-
npx total-recall secret set <key>
|
|
328
|
-
npx total-recall secret rotate <key>
|
|
382
|
+
npx total-recall secret set <key> --stdin # store (0600, AES); --stdin keeps it out of argv/history
|
|
383
|
+
npx total-recall secret rotate <key> --stdin # replace + mark rotated
|
|
329
384
|
npx total-recall secret get <key> # print value (audited)
|
|
330
385
|
npx total-recall secret list # metadata only, no values
|
|
331
|
-
npx total-recall secret
|
|
386
|
+
npx total-recall secret catalog # keys + providers + usage + rotation + cost
|
|
387
|
+
npx total-recall secret export-env --path <repo> # SSOT → local .env
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
**Always prefer `--stdin`** (or `--from-clipboard` for `rotate`). A value passed
|
|
391
|
+
as an argv string lands in shell history, in `ps` output, and in this
|
|
392
|
+
transcript. `set`, `rotate` and `rekey` all accept it.
|
|
393
|
+
|
|
394
|
+
### Master password and `rekey`
|
|
395
|
+
|
|
396
|
+
The store is AES-256-GCM; the key is scrypt-derived (N=2^16, ~64 MB) from
|
|
397
|
+
`TR_SECRETS_PASSWORD`. On macOS the password belongs in the **Keychain**
|
|
398
|
+
(`security add-generic-password -s total-recall-secrets`), not in a dotfile —
|
|
399
|
+
`rekey` picks the Keychain entry up automatically and rotates every carrier
|
|
400
|
+
(LaunchAgent plist, env file, Keychain) in one transaction, verifying the
|
|
401
|
+
retired password no longer decrypts before it commits.
|
|
402
|
+
|
|
403
|
+
> **Processes hold the old password until restarted.** After a rekey, anything
|
|
404
|
+
> still running with the retired value fails with `Failed to decrypt secrets
|
|
405
|
+
> store: Unsupported state or unable to authenticate data`. Restart the daemon
|
|
406
|
+
> and any launchd jobs.
|
|
407
|
+
|
|
408
|
+
The derived key is cached in-process (bounded, keyed on salt **and** password
|
|
409
|
+
digest, so neither a re-encrypt nor a rotation can be served a stale key). This
|
|
410
|
+
matters because the KDF costs ~200 ms and a single dashboard load reads the
|
|
411
|
+
store several times. `TR_SECRETS_KEY_CACHE=0` disables it.
|
|
412
|
+
|
|
413
|
+
### Getting secrets OUT of `.env` files and INTO the store
|
|
414
|
+
|
|
415
|
+
`import-env` is the **one-time migration**, not the steady state:
|
|
416
|
+
|
|
417
|
+
```bash
|
|
418
|
+
npx total-recall secret import-env --dry-run --all # scan every known repo, change nothing
|
|
419
|
+
npx total-recall secret import-env --all # import everything new
|
|
420
|
+
npx total-recall secret import-env KEY1 KEY2 # or just these
|
|
332
421
|
```
|
|
333
422
|
|
|
423
|
+
> **One credential → exactly ONE repo.** The store rejects a multi-repo binding,
|
|
424
|
+
> and `import-env` derives the binding from *where it found the value* — so a
|
|
425
|
+
> key sitting in six repos' `.env` files fails to import and `--repo` will not
|
|
426
|
+
> override it. That is the rule working: one value in six repos means a leak of
|
|
427
|
+
> any one repo burns all six. Either issue a distinct key per app, or bind it to
|
|
428
|
+
> its owning repo explicitly:
|
|
429
|
+
>
|
|
430
|
+
> ```bash
|
|
431
|
+
> printf '%s' "$VALUE" | npx total-recall secret set <key> --stdin --repos <owning-repo>
|
|
432
|
+
> ```
|
|
433
|
+
|
|
434
|
+
Once stored, the flow reverses — `.env` becomes a **projection of the store**:
|
|
435
|
+
|
|
436
|
+
```bash
|
|
437
|
+
npx total-recall secret export-env --path <repo> --dry-run
|
|
438
|
+
npx total-recall secret export-env --all-projects # every registered project
|
|
439
|
+
npx total-recall secret export-env --no-global # only keys bound to this repo
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
By default `export-env` merges only the TR-managed block, leaving hand-written
|
|
443
|
+
lines alone; `--replace-all` overwrites the whole file.
|
|
444
|
+
|
|
445
|
+
### Health gates (all exit non-zero on a finding)
|
|
446
|
+
|
|
447
|
+
```bash
|
|
448
|
+
npx total-recall secret check-surfaces # a secret VALUE leaked into a compiled surface/openwiki
|
|
449
|
+
npx total-recall secret shared # same value reused under several key names / apps
|
|
450
|
+
npx total-recall secret duplicates # duplicate entries
|
|
451
|
+
npx total-recall secret tracking-health # a set secret with no live account/usage binding
|
|
452
|
+
npx total-recall secret rotation-due # overdue keys (--enqueue to create daemon tasks)
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
`shared` is the one that catches real risk: it reports when one credential backs
|
|
456
|
+
multiple keys or apps, which defeats per-app revocation. `secret meta <key>
|
|
457
|
+
--shared-ok` waives it deliberately.
|
|
458
|
+
|
|
459
|
+
**Never** put a secret value in vault markdown, openwiki, a compiled
|
|
460
|
+
INSTRUCTIONS.md, a skill surface, or a `.env.bak-*` file. Backups of `.env` are
|
|
461
|
+
a common accident — they are usually **not** gitignored, so a `git add -A`
|
|
462
|
+
commits live keys.
|
|
463
|
+
|
|
334
464
|
### Executed rotation — `rotate-auto` / `rotate-browser` (added 3.22.0)
|
|
335
465
|
|
|
336
466
|
Rotation used to only ever **print instructions**. It now executes. Every key
|
|
@@ -413,6 +543,68 @@ that dead-ends back at a manual dashboard step.
|
|
|
413
543
|
<!-- END INJECTED MEMORY -->
|
|
414
544
|
|
|
415
545
|
|
|
546
|
+
---
|
|
547
|
+
|
|
548
|
+
## 🕸️ MESH / CONTROL SERVER (`mesh` subcommand)
|
|
549
|
+
|
|
550
|
+
Headscale administration — node inventory, ACL policy, and Tailscale SSH. This
|
|
551
|
+
is how one machine reaches another (laptop → Mac Mini → droplet) without
|
|
552
|
+
hardcoding hostnames anywhere.
|
|
553
|
+
|
|
554
|
+
```bash
|
|
555
|
+
npx total-recall mesh status # control-server reachability + credential state
|
|
556
|
+
npx total-recall mesh nodes # every node and how to reach it
|
|
557
|
+
npx total-recall mesh access <node> # resolved access for one node
|
|
558
|
+
npx total-recall mesh access import # propose access from ~/.ssh/config (--apply to save)
|
|
559
|
+
npx total-recall mesh ssh <node> 'uptime' # run a command on a node
|
|
560
|
+
npx total-recall mesh preauthkey # enrollment key for `tailscale up --authkey`
|
|
561
|
+
npx total-recall mesh policy get
|
|
562
|
+
npx total-recall mesh policy init-ssh --dry-run
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
**Device details are entity fields, never literals.** Hostname, mesh IP, role,
|
|
566
|
+
OS, location, capabilities and latency history live on the node's SSSS
|
|
567
|
+
`mesh_node` document plus live discovery. Product code and UI bind to those
|
|
568
|
+
fields — never to a personal machine name.
|
|
569
|
+
|
|
570
|
+
**Mesh membership is the trust boundary.** Every node admitted to the tailnet
|
|
571
|
+
inherits whatever the policy grants; audit `mesh nodes` before widening it.
|
|
572
|
+
`policy` commands require the control server running with `policy.mode:
|
|
573
|
+
database`.
|
|
574
|
+
|
|
575
|
+
**WAN timeouts.** Laptop↔cloud probes need **≥10 s**. Shorter timeouts (1.5 s/3 s)
|
|
576
|
+
falsely report a reachable cloud node as down while raw `curl /health` succeeds.
|
|
577
|
+
|
|
578
|
+
---
|
|
579
|
+
|
|
580
|
+
## 🧪 TESTS
|
|
581
|
+
|
|
582
|
+
```bash
|
|
583
|
+
npm test # vitest run — whole suite
|
|
584
|
+
npx vitest run <path> # one spec file
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
Current baseline: **288 spec files / 1493 tests, ~160 s** on the Mac Mini.
|
|
588
|
+
|
|
589
|
+
> **Never run the full suite, a full typecheck, or a production build on the
|
|
590
|
+
> laptop.** These go to the Mac Mini or the droplet — local runs cause slowdowns
|
|
591
|
+
> and OOM kills. Sync and run remotely:
|
|
592
|
+
>
|
|
593
|
+
> ```bash
|
|
594
|
+
> rsync -a --delete --exclude=node_modules/ --exclude=.git/ ./ mac-mini:~/github/total-recall/
|
|
595
|
+
> ssh mac-mini 'export PATH=/opt/homebrew/bin:$PATH; cd ~/github/total-recall && npx vitest run'
|
|
596
|
+
> ```
|
|
597
|
+
>
|
|
598
|
+
> A non-login `ssh` shell has no Homebrew on PATH — export it or use `bash -lc`,
|
|
599
|
+
> otherwise the run dies with `command not found: npx` **and still exits 0**.
|
|
600
|
+
|
|
601
|
+
**Never skip tests** to make a release look clean, and never use `--force` on a
|
|
602
|
+
checker. Audit and clean up any rows, files, or mock entries a suite leaves
|
|
603
|
+
behind in a live database.
|
|
604
|
+
|
|
605
|
+
`npx` itself adds ~2 s of resolution overhead per invocation. For tight loops
|
|
606
|
+
call the local binary directly: `node bin/total-recall.mjs <cmd>`.
|
|
607
|
+
|
|
416
608
|
---
|
|
417
609
|
|
|
418
610
|
## Operational modules only (not skills)
|
|
@@ -19,10 +19,10 @@ tags:
|
|
|
19
19
|
- budget
|
|
20
20
|
- sandbox
|
|
21
21
|
- watchdog
|
|
22
|
-
created: 2026-05-25T00:51:00.000Z
|
|
23
|
-
updated: 2026-
|
|
22
|
+
created: '2026-05-25T00:51:00.000Z'
|
|
23
|
+
updated: '2026-01-04T02:42:09.572Z'
|
|
24
24
|
sentiment_polarity: descriptive
|
|
25
|
-
last_accessed: 2026-
|
|
25
|
+
last_accessed: '2026-01-04T02:42:09.572Z'
|
|
26
26
|
source:
|
|
27
27
|
type: manual
|
|
28
28
|
session_id: migration-repair
|
|
@@ -39,7 +39,16 @@ object: Total Recall Command & Architecture Help Reference
|
|
|
39
39
|
decay:
|
|
40
40
|
half_life_days: 180
|
|
41
41
|
access_count: 1
|
|
42
|
-
x_temporal_context: 2026-05-26T23:05:06.474Z
|
|
42
|
+
x_temporal_context: '2026-05-26T23:05:06.474Z'
|
|
43
|
+
description: Total Recall Command & Architecture Help Reference
|
|
44
|
+
timestamp: '2026-05-25T00:56:00.000Z'
|
|
45
|
+
x_citations:
|
|
46
|
+
- source: manual
|
|
47
|
+
title: Total Recall Command & Architecture Help Reference
|
|
48
|
+
url: 'session://migration-repair'
|
|
49
|
+
published: '2026-05-26T23:05:06.474Z'
|
|
50
|
+
relevance: 1
|
|
51
|
+
accessed: '2026-08-18T23:41:45.533Z'
|
|
43
52
|
---
|
|
44
53
|
# Total Recall Command & Architecture Help Reference
|
|
45
54
|
|
package/scaffold/.agent/skills/total-recall/memory-vault/invariants/operating-instructions.md
CHANGED
|
@@ -3,6 +3,8 @@ type: memory
|
|
|
3
3
|
slug: operating-instructions
|
|
4
4
|
category: invariants
|
|
5
5
|
title: Total Recall Core Operating Protocol
|
|
6
|
+
description: "The absolute operating protocol for agents using Total Recall: SSSS-governed memory, CLI-first mutations, no external database."
|
|
7
|
+
timestamp: 2026-05-15T00:00:00.000Z
|
|
6
8
|
schema_version: 2
|
|
7
9
|
status: active
|
|
8
10
|
confidence: 1
|
|
@@ -23,8 +23,7 @@ Rebuild the derived indexes and compile memory surfaces.
|
|
|
23
23
|
### 1.3 `connect`
|
|
24
24
|
Configure an IDE or external client to bind to the Total Recall local server.
|
|
25
25
|
* **Syntax**: `npx total-recall connect <client> [options]`
|
|
26
|
-
* **Supported Clients**: `
|
|
27
|
-
Any host application not listed here connects through the generic `http-api` client.
|
|
26
|
+
* **Supported Clients**: `cursor`, `claude-code`, `antigravity`, `gemini`, `vscode`, `windsurf`, `aider`, `ultrachat`, `obsidian`.
|
|
28
27
|
* **Options**:
|
|
29
28
|
* `--brain <url>`: Override default brain server URL.
|
|
30
29
|
* `--token <PAT>`: Inject bearer PAT credentials for authenticating requests.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{"id":"7478a515-ea8e-4360-93b8-a10cf0642d88","topic":"npm Registry API documentation and developer endpoints","status":"pending","priority":"medium","notes":"Consult official npm documentation for registry APIs, authentication, and package metadata without using raw key strings.","node_slug":null,"research_phase":"acquisition","created_at":"2026-08-18T20:58:08.631Z","updated_at":"2026-08-18T20:58:08.631Z","completed_at":null,"summary":{"id":"7478a515-ea8e-4360-93b8-a10cf0642d88","topic":"npm Registry API documentation and developer endpoints","status":"pending","phase":"acquisition","last_updated":"2026-08-18T20:58:08.631Z","conclusions":[],"ongoing_directions":["Research project is enqueued / in progress in phase: acquisition"],"sources_count":0}}
|
|
2
|
+
{"id":"f9757282-dad8-4c3b-b44d-c796555468c5","topic":"Supabase Client, Auth, and REST APIs","status":"pending","priority":"medium","notes":"Use official Supabase documentation to understand authentication, database, and storage API surfaces.","node_slug":null,"research_phase":"acquisition","created_at":"2026-08-18T20:57:04.280Z","updated_at":"2026-08-18T20:57:04.280Z","completed_at":null,"summary":{"id":"f9757282-dad8-4c3b-b44d-c796555468c5","topic":"Supabase Client, Auth, and REST APIs","status":"pending","phase":"acquisition","last_updated":"2026-08-18T20:57:04.280Z","conclusions":[],"ongoing_directions":["Research project is enqueued / in progress in phase: acquisition"],"sources_count":0}}
|
|
3
|
+
{"id":"e196b27d-c799-4e60-88d0-585c479b6053","topic":"Supabase Database, Auth, Storage, and Management APIs","status":"pending","priority":"medium","notes":"Consult official Supabase documentation for REST API, SDK, and platform integration; do not use raw secret key string.","node_slug":null,"research_phase":"acquisition","created_at":"2026-08-18T20:56:34.347Z","updated_at":"2026-08-18T20:56:34.347Z","completed_at":null,"summary":{"id":"e196b27d-c799-4e60-88d0-585c479b6053","topic":"Supabase Database, Auth, Storage, and Management APIs","status":"pending","phase":"acquisition","last_updated":"2026-08-18T20:56:34.347Z","conclusions":[],"ongoing_directions":["Research project is enqueued / in progress in phase: acquisition"],"sources_count":0}}
|
|
4
|
+
{"id":"f6c787c9-212c-4468-a276-5af7d7d78dbc","topic":"Supabase REST API and Client SDKs","status":"pending","priority":"medium","notes":"Use official Supabase documentation for client SDKs, REST API, Database, and Auth endpoints. Do not search using raw key values.","node_slug":null,"research_phase":"acquisition","created_at":"2026-08-18T20:56:21.400Z","updated_at":"2026-08-18T20:56:21.400Z","completed_at":null,"summary":{"id":"f6c787c9-212c-4468-a276-5af7d7d78dbc","topic":"Supabase REST API and Client SDKs","status":"pending","phase":"acquisition","last_updated":"2026-08-18T20:56:21.400Z","conclusions":[],"ongoing_directions":["Research project is enqueued / in progress in phase: acquisition"],"sources_count":0}}
|
|
5
|
+
{"id":"c95b76a3-9bd3-42fd-8c37-0765cabef6a8","topic":"Documenso document signing and template REST API","status":"pending","priority":"medium","notes":"Consult official Documenso documentation for REST API endpoints, document signing workflows, and API key authentication. Do not query using the raw key string.","node_slug":null,"research_phase":"acquisition","created_at":"2026-08-18T20:55:37.372Z","updated_at":"2026-08-18T20:55:37.372Z","completed_at":null,"summary":{"id":"c95b76a3-9bd3-42fd-8c37-0765cabef6a8","topic":"Documenso document signing and template REST API","status":"pending","phase":"acquisition","last_updated":"2026-08-18T20:55:37.372Z","conclusions":[],"ongoing_directions":["Research project is enqueued / in progress in phase: acquisition"],"sources_count":0}}
|
|
6
|
+
{"id":"9b1f9703-76c3-4743-916a-a3340c2b74f4","topic":"Claude Code messaging API and CLI integration","status":"pending","priority":"medium","notes":"Consult official Anthropic Claude Code documentation for API and integration details; do not use the raw key string.","node_slug":null,"research_phase":"acquisition","created_at":"2026-08-18T20:55:25.048Z","updated_at":"2026-08-18T20:55:25.048Z","completed_at":null,"summary":{"id":"9b1f9703-76c3-4743-916a-a3340c2b74f4","topic":"Claude Code messaging API and CLI integration","status":"pending","phase":"acquisition","last_updated":"2026-08-18T20:55:25.048Z","conclusions":[],"ongoing_directions":["Research project is enqueued / in progress in phase: acquisition"],"sources_count":0}}
|
|
7
|
+
{"id":"0efa65c2-0231-4267-a304-e0285e50011e","topic":"Total Recall repository improvement research: SSSS validated write pipeline, filesystem-native memory engines, local-first semantic indexing, Express 5 API hardening, 2026 agent memory architecture patterns","status":"pending","priority":"high","notes":"Queued from repo-wide analysis request on 2026-06-01. Focus on current best practices and meta-patterns for filesystem-native memory OS, validated operation envelopes, append-only logs, cache invalidation, research queue architecture, and secure local-first AI tools.","node_slug":null,"research_phase":"acquisition","created_at":"2026-06-01T06:07:40.102Z","updated_at":"2026-06-01T06:07:40.126Z","completed_at":null,"summary":{"id":"0efa65c2-0231-4267-a304-e0285e50011e","topic":"Total Recall repository improvement research: SSSS validated write pipeline, filesystem-native memory engines, local-first semantic indexing, Express 5 API hardening, 2026 agent memory architecture patterns","status":"pending","phase":"acquisition","last_updated":"2026-06-01T06:07:40.126Z","conclusions":[],"ongoing_directions":["Research project is enqueued / in progress in phase: acquisition"],"sources_count":0}}
|