daftari 1.30.0 → 1.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. package/CHANGELOG.md +176 -0
  2. package/README.md +188 -4
  3. package/dist/access/rbac.d.ts +1 -0
  4. package/dist/access/rbac.d.ts.map +1 -1
  5. package/dist/access/rbac.js +7 -0
  6. package/dist/access/rbac.js.map +1 -1
  7. package/dist/audit/checks/broken_refs.d.ts +1 -1
  8. package/dist/audit/checks/broken_refs.d.ts.map +1 -1
  9. package/dist/audit/checks/broken_refs.js +77 -22
  10. package/dist/audit/checks/broken_refs.js.map +1 -1
  11. package/dist/audit/collect.d.ts +1 -0
  12. package/dist/audit/collect.d.ts.map +1 -1
  13. package/dist/audit/collect.js +22 -2
  14. package/dist/audit/collect.js.map +1 -1
  15. package/dist/audit/index.d.ts.map +1 -1
  16. package/dist/audit/index.js +9 -3
  17. package/dist/audit/index.js.map +1 -1
  18. package/dist/audit/links.d.ts.map +1 -1
  19. package/dist/audit/links.js +9 -3
  20. package/dist/audit/links.js.map +1 -1
  21. package/dist/audit/report.d.ts.map +1 -1
  22. package/dist/audit/report.js +2 -0
  23. package/dist/audit/report.js.map +1 -1
  24. package/dist/audit/types.d.ts +4 -1
  25. package/dist/audit/types.d.ts.map +1 -1
  26. package/dist/audit/types.js.map +1 -1
  27. package/dist/backfill/derive.d.ts.map +1 -1
  28. package/dist/backfill/derive.js +1 -0
  29. package/dist/backfill/derive.js.map +1 -1
  30. package/dist/cli.d.ts.map +1 -1
  31. package/dist/cli.js +24 -0
  32. package/dist/cli.js.map +1 -1
  33. package/dist/consolidate/index.d.ts.map +1 -1
  34. package/dist/consolidate/index.js +10 -12
  35. package/dist/consolidate/index.js.map +1 -1
  36. package/dist/court/precedent.d.ts.map +1 -1
  37. package/dist/court/precedent.js +6 -1
  38. package/dist/court/precedent.js.map +1 -1
  39. package/dist/curation/consumes.d.ts +24 -0
  40. package/dist/curation/consumes.d.ts.map +1 -0
  41. package/dist/curation/consumes.js +122 -0
  42. package/dist/curation/consumes.js.map +1 -0
  43. package/dist/curation/edge-staleness.d.ts +47 -0
  44. package/dist/curation/edge-staleness.d.ts.map +1 -0
  45. package/dist/curation/edge-staleness.js +258 -0
  46. package/dist/curation/edge-staleness.js.map +1 -0
  47. package/dist/curation/edges.d.ts.map +1 -1
  48. package/dist/curation/edges.js +190 -17
  49. package/dist/curation/edges.js.map +1 -1
  50. package/dist/curation/lint.d.ts +6 -1
  51. package/dist/curation/lint.d.ts.map +1 -1
  52. package/dist/curation/lint.js +87 -24
  53. package/dist/curation/lint.js.map +1 -1
  54. package/dist/curation/provenance.d.ts +2 -0
  55. package/dist/curation/provenance.d.ts.map +1 -1
  56. package/dist/curation/provenance.js +2 -0
  57. package/dist/curation/provenance.js.map +1 -1
  58. package/dist/curation/read-log.d.ts +19 -0
  59. package/dist/curation/read-log.d.ts.map +1 -0
  60. package/dist/curation/read-log.js +120 -0
  61. package/dist/curation/read-log.js.map +1 -0
  62. package/dist/curation/review-throughput.d.ts +24 -0
  63. package/dist/curation/review-throughput.d.ts.map +1 -0
  64. package/dist/curation/review-throughput.js +102 -0
  65. package/dist/curation/review-throughput.js.map +1 -0
  66. package/dist/curation/shadow.d.ts +1 -0
  67. package/dist/curation/shadow.d.ts.map +1 -1
  68. package/dist/curation/shadow.js +17 -11
  69. package/dist/curation/shadow.js.map +1 -1
  70. package/dist/curation/staged-actions.d.ts +12 -1
  71. package/dist/curation/staged-actions.d.ts.map +1 -1
  72. package/dist/curation/staged-actions.js +123 -48
  73. package/dist/curation/staged-actions.js.map +1 -1
  74. package/dist/curation/structural.d.ts +16 -0
  75. package/dist/curation/structural.d.ts.map +1 -0
  76. package/dist/curation/structural.js +64 -0
  77. package/dist/curation/structural.js.map +1 -0
  78. package/dist/curation/tension-blast.d.ts +4 -0
  79. package/dist/curation/tension-blast.d.ts.map +1 -1
  80. package/dist/curation/tension-blast.js +9 -0
  81. package/dist/curation/tension-blast.js.map +1 -1
  82. package/dist/curation/tension.d.ts +5 -2
  83. package/dist/curation/tension.d.ts.map +1 -1
  84. package/dist/curation/tension.js +68 -25
  85. package/dist/curation/tension.js.map +1 -1
  86. package/dist/curation/tier0.d.ts +27 -0
  87. package/dist/curation/tier0.d.ts.map +1 -0
  88. package/dist/curation/tier0.js +196 -0
  89. package/dist/curation/tier0.js.map +1 -0
  90. package/dist/curation/tier1.d.ts +30 -0
  91. package/dist/curation/tier1.d.ts.map +1 -0
  92. package/dist/curation/tier1.js +142 -0
  93. package/dist/curation/tier1.js.map +1 -0
  94. package/dist/curation/tier2.d.ts +43 -0
  95. package/dist/curation/tier2.d.ts.map +1 -0
  96. package/dist/curation/tier2.js +182 -0
  97. package/dist/curation/tier2.js.map +1 -0
  98. package/dist/curation/vault-docs.d.ts +5 -1
  99. package/dist/curation/vault-docs.d.ts.map +1 -1
  100. package/dist/curation/vault-docs.js +14 -0
  101. package/dist/curation/vault-docs.js.map +1 -1
  102. package/dist/eval/generate.d.ts.map +1 -1
  103. package/dist/eval/generate.js +38 -5
  104. package/dist/eval/generate.js.map +1 -1
  105. package/dist/eval/index.d.ts +1 -0
  106. package/dist/eval/index.d.ts.map +1 -1
  107. package/dist/eval/index.js +249 -50
  108. package/dist/eval/index.js.map +1 -1
  109. package/dist/eval/llm-openrouter.d.ts.map +1 -1
  110. package/dist/eval/llm-openrouter.js +158 -65
  111. package/dist/eval/llm-openrouter.js.map +1 -1
  112. package/dist/eval/llm.js +1 -1
  113. package/dist/eval/llm.js.map +1 -1
  114. package/dist/eval/prune.d.ts +24 -0
  115. package/dist/eval/prune.d.ts.map +1 -0
  116. package/dist/eval/prune.js +83 -0
  117. package/dist/eval/prune.js.map +1 -0
  118. package/dist/eval/storage.d.ts +3 -0
  119. package/dist/eval/storage.d.ts.map +1 -1
  120. package/dist/eval/storage.js +14 -0
  121. package/dist/eval/storage.js.map +1 -1
  122. package/dist/frontmatter/schema.d.ts.map +1 -1
  123. package/dist/frontmatter/schema.js +19 -1
  124. package/dist/frontmatter/schema.js.map +1 -1
  125. package/dist/frontmatter/types.d.ts +4 -1
  126. package/dist/frontmatter/types.d.ts.map +1 -1
  127. package/dist/frontmatter/types.js +8 -0
  128. package/dist/frontmatter/types.js.map +1 -1
  129. package/dist/import/index.d.ts.map +1 -1
  130. package/dist/import/index.js +44 -9
  131. package/dist/import/index.js.map +1 -1
  132. package/dist/import/langgraph-store.d.ts +53 -0
  133. package/dist/import/langgraph-store.d.ts.map +1 -0
  134. package/dist/import/langgraph-store.js +292 -0
  135. package/dist/import/langgraph-store.js.map +1 -0
  136. package/dist/index.d.ts +5 -0
  137. package/dist/index.d.ts.map +1 -1
  138. package/dist/index.js +56 -18
  139. package/dist/index.js.map +1 -1
  140. package/dist/interview/index.d.ts +7 -0
  141. package/dist/interview/index.d.ts.map +1 -0
  142. package/dist/interview/index.js +219 -0
  143. package/dist/interview/index.js.map +1 -0
  144. package/dist/interview/questions.d.ts +18 -0
  145. package/dist/interview/questions.d.ts.map +1 -0
  146. package/dist/interview/questions.js +169 -0
  147. package/dist/interview/questions.js.map +1 -0
  148. package/dist/interview/transcript.d.ts +23 -0
  149. package/dist/interview/transcript.d.ts.map +1 -0
  150. package/dist/interview/transcript.js +155 -0
  151. package/dist/interview/transcript.js.map +1 -0
  152. package/dist/lifecycle/lock.d.ts +9 -1
  153. package/dist/lifecycle/lock.d.ts.map +1 -1
  154. package/dist/lifecycle/lock.js +34 -9
  155. package/dist/lifecycle/lock.js.map +1 -1
  156. package/dist/okf/export.d.ts +12 -0
  157. package/dist/okf/export.d.ts.map +1 -0
  158. package/dist/okf/export.js +117 -0
  159. package/dist/okf/export.js.map +1 -0
  160. package/dist/okf/import.d.ts +24 -0
  161. package/dist/okf/import.d.ts.map +1 -0
  162. package/dist/okf/import.js +115 -0
  163. package/dist/okf/import.js.map +1 -0
  164. package/dist/okf/index.d.ts +2 -0
  165. package/dist/okf/index.d.ts.map +1 -0
  166. package/dist/okf/index.js +159 -0
  167. package/dist/okf/index.js.map +1 -0
  168. package/dist/okf/map.d.ts +27 -0
  169. package/dist/okf/map.d.ts.map +1 -0
  170. package/dist/okf/map.js +327 -0
  171. package/dist/okf/map.js.map +1 -0
  172. package/dist/okf/types.d.ts +13 -0
  173. package/dist/okf/types.d.ts.map +1 -0
  174. package/dist/okf/types.js +82 -0
  175. package/dist/okf/types.js.map +1 -0
  176. package/dist/search/coverage.d.ts.map +1 -1
  177. package/dist/search/coverage.js +1 -1
  178. package/dist/search/coverage.js.map +1 -1
  179. package/dist/search/hybrid.d.ts +19 -0
  180. package/dist/search/hybrid.d.ts.map +1 -1
  181. package/dist/search/hybrid.js +60 -15
  182. package/dist/search/hybrid.js.map +1 -1
  183. package/dist/search/reindex.d.ts +4 -0
  184. package/dist/search/reindex.d.ts.map +1 -1
  185. package/dist/search/reindex.js +64 -14
  186. package/dist/search/reindex.js.map +1 -1
  187. package/dist/search/watcher.d.ts +2 -0
  188. package/dist/search/watcher.d.ts.map +1 -1
  189. package/dist/search/watcher.js +29 -27
  190. package/dist/search/watcher.js.map +1 -1
  191. package/dist/serve/index.d.ts +36 -0
  192. package/dist/serve/index.d.ts.map +1 -0
  193. package/dist/serve/index.js +507 -0
  194. package/dist/serve/index.js.map +1 -0
  195. package/dist/server.d.ts +10 -1
  196. package/dist/server.d.ts.map +1 -1
  197. package/dist/server.js +89 -14
  198. package/dist/server.js.map +1 -1
  199. package/dist/sleep/index.d.ts +2 -0
  200. package/dist/sleep/index.d.ts.map +1 -1
  201. package/dist/sleep/index.js +212 -21
  202. package/dist/sleep/index.js.map +1 -1
  203. package/dist/sleep/report.d.ts +3 -0
  204. package/dist/sleep/report.d.ts.map +1 -1
  205. package/dist/sleep/report.js +52 -0
  206. package/dist/sleep/report.js.map +1 -1
  207. package/dist/sleep/tension-scan-state.d.ts +19 -0
  208. package/dist/sleep/tension-scan-state.d.ts.map +1 -0
  209. package/dist/sleep/tension-scan-state.js +0 -0
  210. package/dist/sleep/tension-scan-state.js.map +1 -0
  211. package/dist/sleep/tension-scan.d.ts +44 -0
  212. package/dist/sleep/tension-scan.d.ts.map +1 -0
  213. package/dist/sleep/tension-scan.js +0 -0
  214. package/dist/sleep/tension-scan.js.map +1 -0
  215. package/dist/storage/backend.d.ts +12 -0
  216. package/dist/storage/backend.d.ts.map +1 -0
  217. package/dist/storage/backend.js +231 -0
  218. package/dist/storage/backend.js.map +1 -0
  219. package/dist/storage/backends/fs.d.ts +4 -0
  220. package/dist/storage/backends/fs.d.ts.map +1 -0
  221. package/dist/storage/backends/fs.js +99 -0
  222. package/dist/storage/backends/fs.js.map +1 -0
  223. package/dist/storage/fs-walk.d.ts +5 -0
  224. package/dist/storage/fs-walk.d.ts.map +1 -0
  225. package/dist/storage/fs-walk.js +28 -0
  226. package/dist/storage/fs-walk.js.map +1 -0
  227. package/dist/storage/index-db.d.ts +16 -0
  228. package/dist/storage/index-db.d.ts.map +1 -1
  229. package/dist/storage/index-db.js +95 -5
  230. package/dist/storage/index-db.js.map +1 -1
  231. package/dist/storage/local.d.ts +1 -0
  232. package/dist/storage/local.d.ts.map +1 -1
  233. package/dist/storage/local.js +12 -0
  234. package/dist/storage/local.js.map +1 -1
  235. package/dist/storage/sync.d.ts +25 -0
  236. package/dist/storage/sync.d.ts.map +1 -0
  237. package/dist/storage/sync.js +308 -0
  238. package/dist/storage/sync.js.map +1 -0
  239. package/dist/sync/index.d.ts +2 -0
  240. package/dist/sync/index.d.ts.map +1 -0
  241. package/dist/sync/index.js +136 -0
  242. package/dist/sync/index.js.map +1 -0
  243. package/dist/themes/clustering.d.ts +6 -11
  244. package/dist/themes/clustering.d.ts.map +1 -1
  245. package/dist/themes/clustering.js +76 -84
  246. package/dist/themes/clustering.js.map +1 -1
  247. package/dist/tools/consumes.d.ts +14 -0
  248. package/dist/tools/consumes.d.ts.map +1 -0
  249. package/dist/tools/consumes.js +99 -0
  250. package/dist/tools/consumes.js.map +1 -0
  251. package/dist/tools/curation.d.ts +2 -0
  252. package/dist/tools/curation.d.ts.map +1 -1
  253. package/dist/tools/curation.js +53 -15
  254. package/dist/tools/curation.js.map +1 -1
  255. package/dist/tools/edge-staleness.d.ts +28 -0
  256. package/dist/tools/edge-staleness.d.ts.map +1 -0
  257. package/dist/tools/edge-staleness.js +205 -0
  258. package/dist/tools/edge-staleness.js.map +1 -0
  259. package/dist/tools/edges.d.ts.map +1 -1
  260. package/dist/tools/edges.js +27 -21
  261. package/dist/tools/edges.js.map +1 -1
  262. package/dist/tools/read.d.ts +15 -1
  263. package/dist/tools/read.d.ts.map +1 -1
  264. package/dist/tools/read.js +211 -76
  265. package/dist/tools/read.js.map +1 -1
  266. package/dist/tools/receipt.d.ts.map +1 -1
  267. package/dist/tools/receipt.js +16 -7
  268. package/dist/tools/receipt.js.map +1 -1
  269. package/dist/tools/search.d.ts +1 -0
  270. package/dist/tools/search.d.ts.map +1 -1
  271. package/dist/tools/search.js +147 -5
  272. package/dist/tools/search.js.map +1 -1
  273. package/dist/tools/staged-actions.d.ts +3 -0
  274. package/dist/tools/staged-actions.d.ts.map +1 -1
  275. package/dist/tools/staged-actions.js +225 -22
  276. package/dist/tools/staged-actions.js.map +1 -1
  277. package/dist/tools/themes.d.ts +8 -0
  278. package/dist/tools/themes.d.ts.map +1 -1
  279. package/dist/tools/themes.js +208 -122
  280. package/dist/tools/themes.js.map +1 -1
  281. package/dist/tools/tier1.d.ts +14 -0
  282. package/dist/tools/tier1.d.ts.map +1 -0
  283. package/dist/tools/tier1.js +169 -0
  284. package/dist/tools/tier1.js.map +1 -0
  285. package/dist/tools/tier2.d.ts +27 -0
  286. package/dist/tools/tier2.d.ts.map +1 -0
  287. package/dist/tools/tier2.js +344 -0
  288. package/dist/tools/tier2.js.map +1 -0
  289. package/dist/tools/write.d.ts +9 -1
  290. package/dist/tools/write.d.ts.map +1 -1
  291. package/dist/tools/write.js +596 -174
  292. package/dist/tools/write.js.map +1 -1
  293. package/dist/utils/config.d.ts +52 -0
  294. package/dist/utils/config.d.ts.map +1 -1
  295. package/dist/utils/config.js +394 -24
  296. package/dist/utils/config.js.map +1 -1
  297. package/dist/utils/dates.d.ts +1 -0
  298. package/dist/utils/dates.d.ts.map +1 -1
  299. package/dist/utils/dates.js +5 -0
  300. package/dist/utils/dates.js.map +1 -1
  301. package/dist/utils/run-id.d.ts +3 -0
  302. package/dist/utils/run-id.d.ts.map +1 -0
  303. package/dist/utils/run-id.js +19 -0
  304. package/dist/utils/run-id.js.map +1 -0
  305. package/dist/utils/vault-gitignore.d.ts +1 -1
  306. package/dist/utils/vault-gitignore.d.ts.map +1 -1
  307. package/dist/utils/vault-gitignore.js +4 -1
  308. package/dist/utils/vault-gitignore.js.map +1 -1
  309. package/package.json +21 -1
  310. package/templates/config.yaml +8 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,180 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.32.0] - 2026-07-25
11
+
12
+ The interview release: the vault interrogates its principal. `daftari
13
+ interview` turns the signals the vault already computes — open tensions,
14
+ expired canon, unanswered `questions_raised` — into a question sheet, asks
15
+ you at the terminal, and folds the verbatim answers back into the corpus as
16
+ first-class source material. Plus the OKF bridge learns spec v0.2's trust
17
+ signals. Design record:
18
+ `docs/superpowers/specs/2026-07-25-principal-interview-design.md`.
19
+
20
+ ### Added
21
+
22
+ - **`daftari interview` — the principal interview.** The design steal is the
23
+ companion interview on Terence Tao's AI-views "living summary" page: after
24
+ an assistant compiles a corpus of someone's positions, its highest-value
25
+ next move is to ask the principal about the places the corpus is unclear
26
+ and record the answers verbatim. Daftari already computes "unclear" three
27
+ ways, so the sheet is assembled deterministically (LLM-free, the circadian
28
+ precedent — nothing on this surface can spend): open tensions (contested
29
+ first, longest-carried first; legacy `unspecified` and system-generated
30
+ `inter-proposal` entries excluded), canonical accumulation docs past their
31
+ `ttl_days` (largest overshoot first; generative decay is expected, never
32
+ asked about), and `questions_raised` entries no document's
33
+ `questions_answered` covers (duplicate raisings merged). `daftari
34
+ interview` prints the sheet read-only; `daftari interview ask` conducts
35
+ the session and records answers **verbatim** as a vault document in an
36
+ `interviews/` collection — `tier: source` (body immutable), `provenance:
37
+ direct`, `ttl_days: null` (testimony doesn't expire), `sources` tracing
38
+ each question back to its prompting signal, `questions_answered` carrying
39
+ the question texts so a later sheet never re-asks. The write round-trips
40
+ the parser before touching disk, is confined by the same vault-path gate
41
+ as every other write, and auto-commits honoring `auto_commit`/`git_dir`.
42
+ Recording testimony resolves nothing: the CLI ends by pointing at
43
+ `daftari court rule <id> --references <transcript>` and at re-verified
44
+ writes — a tension may never masquerade as a supersession. Operator-only
45
+ like the court (no access context, no MCP tool); the `--collection` flag
46
+ is allow-listed (one path segment, reserved names rejected) so the
47
+ transcript can neither respell its RBAC collection nor land where vault
48
+ scans can't see it.
49
+
50
+ ### Changed
51
+
52
+ - **OKF bridge upgraded to spec v0.2 — trust signals.** OKF v0.2 adds opt-in
53
+ trust metadata (raw credibility indicators, never computed scores); the
54
+ `daftari okf` bridge now speaks it in both directions. Export derives the
55
+ signals from native metadata: `updated`/`updated_by` become
56
+ `generated: {by, at}` (the spec's rename of v0.1 `timestamp`), the Daftari
57
+ lifecycle maps onto `status: draft|stable|deprecated`
58
+ (canonical → stable; deprecated/superseded/archived → deprecated),
59
+ `ttl_days` becomes an absolute `stale_after` date anchored at `updated`, and
60
+ `sources` become structured `{id, resource}` entries. `verified` is never
61
+ fabricated — Daftari records authorship, not independent confirmations, so
62
+ exported docs honestly carry the "unverified" trust tier. Import understands
63
+ the signals: `generated.at` dates the doc (falling back to `timestamp` per
64
+ spec), a human-reviewed doc (`human:*` in `verified`) lands with
65
+ `confidence: high` (positive signals only ever raise confidence — absence
66
+ keeps the `medium` default, since an unverified v0.2 doc is
67
+ indistinguishable from a v0.1 doc), `status: deprecated` imports as
68
+ `deprecated` instead of resurfacing as a fresh draft, and `stale_after`
69
+ converts to `ttl_days`. The new `Attested Computation` concept type is
70
+ surfaced in an import warning but never auto-elevated to a write-protected
71
+ tier — `tier: source` is enforcement whose only sanctioned grant path is
72
+ `vault_set_tier` (reason required, provenance-logged), and a foreign
73
+ bundle's self-declared `type` must not buy it; the operator reviews and
74
+ elevates deliberately. OKF
75
+ fields with no lossless Daftari slot (the trust record, the attestation
76
+ machinery, unknown producer fields) are preserved under `okf_*` keys, so
77
+ import stays non-destructive. v0.1 bundles import unchanged.
78
+
79
+ ## [1.31.0] - 2026-07-22
80
+
81
+ The self-hosted deployment release: one always-on `daftari serve` instance
82
+ over Streamable HTTP with per-session identity (static tokens and/or OAuth
83
+ 2.1), plus durable object-storage backing (S3/Azure/GCS-interop) behind the
84
+ canonical local git working copy. Design record:
85
+ `docs/superpowers/specs/2026-07-20-self-hosted-server-mode-design.md`.
86
+
87
+ ### Added
88
+
89
+ - **`daftari serve` — server mode over Streamable HTTP (#5).** One always-on
90
+ instance, many MCP clients, per-session RBAC identity resolved at session
91
+ open (zero tool changes — every existence-disclosure invariant applies per
92
+ connection). Static bearer tokens declared in config with values in env
93
+ vars. Fail-loud posture throughout: a non-loopback bind refuses without
94
+ auth AND an explicit `server.transport_security: external` declaration
95
+ (daftari never terminates TLS); with auth configured a bad credential is a
96
+ 401 at session open, never a guest downgrade; loopback binds carry a
97
+ DNS-rebinding guard (Host/Origin validated before routing). The process
98
+ lock learns modes: a stray stdio invocation refuses against a live server,
99
+ a new serve refuses against any live holder, and deliberate replacement is
100
+ `daftari serve --takeover` (stdio-vs-stdio keeps its classic takeover;
101
+ stale locks are still overwritten silently).
102
+ - **OAuth 2.1 resource-server auth for serve (#7).** Bearer JWTs verified
103
+ against your IdP's JWKS (issuer + audience + signature + expiry via
104
+ `jose`), with the subject claim mapped through a config-declared subjects
105
+ table. A valid token with an unmapped subject is 403 — authenticated, not
106
+ authorized — never a guest, never a default role. Composes with static
107
+ tokens (agents on tokens, humans through the IdP). Hardened: https-only
108
+ issuer/JWKS URLs (loopback http for test IdPs only), constant-time static
109
+ token matching, `Object.prototype`-collision-proof subject lookup.
110
+ - **Pluggable storage backends as sync targets (#6).** The local git working
111
+ copy stays canonical; a backend is a dumb `get/put/list/delete` target.
112
+ Backends: `fs` (local/mounted directory), `s3` (S3, MinIO, R2, GCS via its
113
+ S3-interop endpoint), `azure` (Blob/ADLS Gen2) — cloud SDKs are optional
114
+ peer dependencies loaded on demand, credentials come only from the SDKs'
115
+ environment chains, custom endpoints must be https. `daftari sync` pushes
116
+ incrementally against a remote hash manifest (tree + `.git` + durable
117
+ `.daftari` journals; the rebuildable index/locks never sync) and
118
+ `daftari sync --restore` rebuilds a vault into an empty directory with
119
+ per-object hash verification and a reindex. `daftari serve` can push on a
120
+ cadence (`storage.sync_interval_minutes`). Security posture: `.git/config`
121
+ and `.git/hooks` are never synced or restored (git executes what they
122
+ declare — a backup channel must not deliver code), and restore re-enforces
123
+ every exclusion against the untrusted manifest.
124
+ - **Agent-as-judge rerank pool on `vault_search` (#3).** Opt-in
125
+ `rerank_candidates`: the server prepares the fused candidate pool and the
126
+ protocol; the calling agent is the judge.
127
+ - **Per-document theme distributions (#58).** True multi-theme membership via
128
+ chunk-level clustering.
129
+ - **Configurable tool-exposure tiers (#103, #104).** `tools.tier`
130
+ (`core`/`standard`/`full`) plus `include`/`exclude` lists in config.
131
+ - **Resumable reindex (#54).** Per-batch embedding commits so an interrupted
132
+ reindex resumes instead of restarting.
133
+ - **`daftari eval prune` (#100)** — `results/` and `scores/` housekeeping —
134
+ and eval v1 polish (#102): honest exit codes, skipped-run reporting, resume
135
+ validation, tier-aware triviality, intra-call dedupe, `--max-nodes`.
136
+ - **Advisory domain-boundary enforcement (#4)** and inline structural decay
137
+ checks (orphans, deprecated-still-linked) in the curation engine.
138
+ - **Advisory supersede nudge** when overwriting an existing doc, and a YAML
139
+ config error hint for comment lines that lost their `#`.
140
+ - **`daftari okf export|import` — Open Knowledge Format bridge.** OKF is Google
141
+ Cloud's vendor-neutral spec (v0.1) for the LLM-wiki pattern — a directory of
142
+ markdown files with YAML frontmatter — which is exactly Daftari's storage
143
+ model. `okf export` renders a vault as a portable OKF bundle: each doc becomes
144
+ an OKF concept doc carrying the core `type` / `title` / `description` /
145
+ `resource` / `tags` / `timestamp` fields plus a verbatim `daftari` sidecar for
146
+ lossless round-trip, alongside generated `index.md` and `log.md` reserved
147
+ files; the source vault is never mutated. `okf import` adopts an OKF bundle
148
+ into a vault — a bundle from `okf export` round-trips exactly via its sidecar,
149
+ while a foreign bundle is mapped conservatively (docs land as `draft` in the
150
+ `accumulation` domain, the source OKF `type` preserved in `okf_type`); writes
151
+ auto-commit and the index is rebuilt. `--dry-run` previews the import plan.
152
+
153
+ - **The compilation pipeline (#232–#236, #141, #217).** Edge provenance
154
+ classes with type-directed change dispatch (tier 1), structure-first tier-0
155
+ lint checks with a ratify gate, a semantic-review queue with typed verdicts
156
+ (tier 2), the compiled `consumes` dependency graph (run-correlated reads ×
157
+ writes, #233), edge staleness classes and the broken-read rate (#234),
158
+ proposal deltas (write action type, `run_id` provenance, inter-proposal
159
+ tension, a propose-only role, #235), write-protection tiers
160
+ (source/compiled/manual with a demote-then-write escape hatch, #141), a
161
+ review-throughput lint aggregate, and edge-graph existence disclosure
162
+ (blast B′, edges/lint omission) extending the 2026-07-14 RBAC spec (#217).
163
+ - **`sleep` tension-scan dream-type (#228)** closing the day-0 detection gap,
164
+ a langgraph-store adapter with the dedup-is-not-epistemics demo (#227),
165
+ the daftari.dev landing page (#226), and MCP Registry metadata (#222).
166
+ - **Dev maturity (#238–#242):** e2e gate on the built server, tag-driven npm
167
+ release via OIDC trusted publishing, automated code + security review on
168
+ PRs, an `@claude` mention responder, and a nightly maintenance loop.
169
+
170
+ ### Fixed
171
+
172
+ - Canonical-path tension/provenance gates in `vault_status`, `vault_receipt`,
173
+ and precedent lookups (#216), and tier-1 anchors that are unreadable leak
174
+ neither provenance metadata nor existence (#252).
175
+ - `daftari audit` no longer flags on-disk assets and unaudited siblings as
176
+ missing (#255); eval exit codes and resume validation honesty (#102).
177
+
178
+ ### Performance
179
+
180
+ - SQL-authoritative edge reads with write-through and stat-marker self-heal
181
+ (#236), FTS5 `snippet()` for lexical excerpts on the chunk path, and the
182
+ file watcher filters non-markdown events at watch time (#107).
183
+
10
184
  ## [1.30.0] - 2026-07-13
11
185
 
12
186
  ### Added
@@ -1377,6 +1551,8 @@ vault to AI agents, exposing 13 tools over stdio.
1377
1551
  example documents, git history, search index); `daftari --vault` serves it.
1378
1552
  - 160 tests covering all 13 tools and their supporting modules.
1379
1553
 
1554
+ [1.32.0]: https://github.com/mavaali/daftari/releases/tag/v1.32.0
1555
+ [1.31.0]: https://github.com/mavaali/daftari/releases/tag/v1.31.0
1380
1556
  [1.5.1]: https://github.com/mavaali/daftari/releases/tag/v1.5.1
1381
1557
  [1.4.0]: https://github.com/mavaali/daftari/releases/tag/v1.4.0
1382
1558
  [1.1.1]: https://github.com/mavaali/daftari/releases/tag/v1.1.1
package/README.md CHANGED
@@ -116,6 +116,31 @@ The curation engine is advisory: `vault_lint` reports problems and
116
116
  `vault_tension_log` records contradictions. Neither auto-fixes anything. Every
117
117
  change is a deliberate, attributable act.
118
118
 
119
+ ### Tool tiers
120
+
121
+ Every advertised tool costs the agent context tokens and a decision branch.
122
+ The `tools` block in `.daftari/config.yaml` picks how much of the registry
123
+ `tools/list` advertises:
124
+
125
+ ```yaml
126
+ tools:
127
+ tier: standard # core | standard | full (default: full)
128
+ include: # add tools the tier omits
129
+ - vault_tension_log
130
+ exclude: # remove tools the tier includes — exclude wins
131
+ - vault_status
132
+ ```
133
+
134
+ `core` is the search-before-derive loop (`vault_search`, `vault_read`,
135
+ `vault_write`, `vault_index`, `vault_lint`, `vault_status`); `standard` adds
136
+ the full document lifecycle (append/promote/deprecate/supersede/merge,
137
+ confidence and tier setters, propose/ratify) plus diagnostics; `full` is
138
+ everything. `--tools <tier>` overrides the tier for one invocation. Filtering
139
+ only changes what is *advertised* — calls to any registered tool still work,
140
+ so an agent holding a cached tool name keeps working across a tier change.
141
+ Unknown names in `include`/`exclude` warn at startup and are ignored, so a
142
+ config written for a newer daftari still loads.
143
+
119
144
  **Evaluate (opt-in, requires an Anthropic API key):** `daftari eval` — scores how
120
145
  well an LLM can use the curation surface to answer multi-hop questions about the
121
146
  vault. See the [design spec](docs/superpowers/specs/2026-05-31-cortex-quality-metric-design.md)
@@ -243,6 +268,129 @@ daftari backfill --plan
243
268
  daftari backfill --apply --scope specs
244
269
  ```
245
270
 
271
+ ## Open Knowledge Format (OKF)
272
+
273
+ [OKF](https://cloud.google.com/blog/products/data-analytics/how-the-open-knowledge-format-can-improve-data-sharing/)
274
+ is Google Cloud's vendor-neutral spec for the LLM-wiki pattern: a directory of
275
+ markdown files with YAML frontmatter that any producer can emit and any consumer
276
+ can read without translation. A Daftari vault *is* that pattern, so `daftari
277
+ okf` bridges the two directions.
278
+ [v0.2](https://cloud.google.com/blog/products/data-analytics/okf-v0-2-adds-trust-signals/)
279
+ adds opt-in **trust signals** — raw credibility indicators (provenance,
280
+ verification, lifecycle, freshness), never computed scores — and Daftari maps
281
+ them from/to its native metadata.
282
+
283
+ **Export** renders the vault as a portable OKF bundle — every doc becomes an OKF
284
+ concept doc (the core `type` / `title` / `description` / `resource` / `tags`
285
+ fields, plus a verbatim `daftari` sidecar for lossless round-trip), with
286
+ generated `index.md` (progressive-disclosure listing) and `log.md`
287
+ (chronological history). Trust signals derive from native metadata:
288
+ `updated`/`updated_by` become `generated`, the lifecycle maps to
289
+ `draft`/`stable`/`deprecated`, `ttl_days` becomes an absolute `stale_after`
290
+ date, and `sources` become structured entries. `verified` is never fabricated —
291
+ Daftari records authorship, not independent confirmations, so exported docs
292
+ honestly read as unverified. The source vault is never mutated.
293
+
294
+ ```bash
295
+ daftari okf export ./my-vault --out ./okf-bundle
296
+ daftari okf export ./my-vault --out ./okf-bundle --collection pricing
297
+ ```
298
+
299
+ **Import** adopts an OKF bundle into a vault. A bundle produced by `okf export`
300
+ round-trips exactly via its sidecar; a foreign bundle is mapped conservatively
301
+ (docs land as `draft` in the `accumulation` domain, the original OKF `type` is
302
+ preserved in an `okf_type` field). Trust signals inform the mapping: a
303
+ human-reviewed doc (a `human:*` entry in `verified`) imports with `confidence:
304
+ high`, `status: deprecated` stays `deprecated` (retired knowledge must not
305
+ resurface as a fresh draft), and `stale_after` converts to `ttl_days`. An
306
+ `Attested Computation` is surfaced in an import warning but **not**
307
+ auto-write-protected: `tier: source` is enforcement, and its only sanctioned
308
+ grant path is `vault_set_tier` (reason required, provenance-logged) — a foreign
309
+ bundle's self-declared `type` doesn't buy it. Review the doc, then elevate
310
+ deliberately. OKF fields with no lossless Daftari slot (the trust record,
311
+ attestation machinery) are preserved under `okf_*` keys. Writes auto-commit and
312
+ the index is rebuilt.
313
+
314
+ ```bash
315
+ daftari okf import ./okf-bundle --into ./my-vault --dry-run
316
+ daftari okf import ./okf-bundle --into ./my-vault
317
+ ```
318
+
319
+ ## Server mode (self-hosted)
320
+
321
+ By default daftari is a per-user stdio process. `daftari serve` starts the
322
+ same vault as **one always-on instance over Streamable HTTP** — for a team, or
323
+ for clients that can't spawn a local process (claude.ai web/mobile, Cowork):
324
+
325
+ ```bash
326
+ daftari serve --vault ./my-vault # http://127.0.0.1:8787/mcp
327
+ daftari serve --vault ./my-vault --bind 0.0.0.0 --port 9000
328
+ ```
329
+
330
+ Identity is **per MCP session**, resolved when the session opens, so every
331
+ RBAC and existence-disclosure rule applies per connection with zero tool
332
+ changes. Two composable auth schemes, both declared in config:
333
+
334
+ ```yaml
335
+ server:
336
+ transport_security: external # required off-loopback: TLS terminates upstream
337
+ auth:
338
+ tokens: # static bearer tokens (agents, ETL)
339
+ - env: DAFTARI_TOKEN_ETL # the VALUE lives in this env var, never in config
340
+ user: agent:etl
341
+ role: admin
342
+ oauth: # OAuth 2.1 resource server (humans via your IdP)
343
+ issuer: https://idp.example.com
344
+ audience: daftari
345
+ jwks_uri: https://idp.example.com/.well-known/jwks.json
346
+ subjects:
347
+ "alice@example.com": { user: human:alice, role: analyst }
348
+ ```
349
+
350
+ The posture is fail-loud, never silent-downgrade: a non-loopback bind refuses
351
+ to start without auth **and** the explicit `transport_security: external`
352
+ acknowledgment (daftari never terminates TLS itself — put Caddy/nginx/a load
353
+ balancer in front); with any auth configured, a bad or missing credential is
354
+ a 401 at session open and a valid JWT whose subject isn't mapped is a 403 —
355
+ neither ever becomes the guest. Guest sessions exist only in the no-auth
356
+ loopback configuration. A running server also refuses to be silently killed:
357
+ stray stdio invocations against the same vault refuse to start, and replacing
358
+ a live holder requires a deliberate `daftari serve --takeover`.
359
+
360
+ ## Storage backing
361
+
362
+ A self-hosted vault can push to durable object storage (#6). The local git
363
+ working copy stays canonical — reads, writes, auto-commits, locks, and the
364
+ index all stay exactly as above — and the backing is a dumb sync target:
365
+
366
+ ```yaml
367
+ # .daftari/config.yaml
368
+ storage:
369
+ backend: s3 # fs | s3 | azure
370
+ bucket: team-vault
371
+ region: us-east-1
372
+ # endpoint: https://… # MinIO / R2 / GCS S3-interop endpoints
373
+ # sync_interval_minutes: 15 # daftari serve pushes on this cadence
374
+ ```
375
+
376
+ ```bash
377
+ daftari sync --vault ./my-vault # incremental push
378
+ daftari sync --vault ./my-vault --dry-run # show the diff only
379
+ daftari sync --vault ./empty-dir --restore --backend s3 --bucket team-vault
380
+ ```
381
+
382
+ The push covers the markdown tree, the `.git` directory, and durable
383
+ `.daftari` journals; the rebuildable SQLite index and lock files never sync.
384
+ Neither do `.git/config` and `.git/hooks` — git *executes* what those
385
+ declare, and a backup channel must not deliver code (`git clone` refuses to
386
+ transmit them for the same reason), so re-add remotes and local git config
387
+ by hand after a restore.
388
+ Cloud backends load their SDKs (`@aws-sdk/client-s3`, `@azure/storage-blob`)
389
+ as optional dependencies — install the one you use; credentials come from the
390
+ SDK's standard environment chain, never from vault config. GCS is reached via
391
+ its S3-interoperability endpoint. Restore refuses non-empty directories and
392
+ reindexes when done.
393
+
246
394
  ## How it compares
247
395
 
248
396
  | |AGENTS.md |RAG |Daftari |
@@ -257,12 +405,18 @@ daftari backfill --apply --scope specs
257
405
 
258
406
  Deliberately deferred to keep the surface tight:
259
407
 
260
- - **Cloud-hosted multi-tenant server** with S3/GCS backend and token auth
261
- - **Remote MCP transport** for claude.ai web, mobile, and Cowork (v1 is a local desktop extension for Claude Desktop and Claude Code)
408
+ - **Cloud-hosted multi-tenant SaaS** (self-hosted server mode HAS shipped
409
+ see [Server mode](#server-mode-self-hosted) and
410
+ [Storage backing](#storage-backing); the project itself hosts nothing)
262
411
  - **Conflict resolution beyond file-level locks** (CRDTs, semantic merge)
263
412
  - **Background curation agent** running lint on a cadence
264
- - **LLM reranking** of search results
265
- - **Enforced domain separation** (v1 documents the convention; v2 enforces it)
413
+ - **Enforced domain separation** (v1 documents the convention; v2 enforces it —
414
+ advisory boundary warnings shipped in the meantime: `vault_lint`'s
415
+ `domainLeaks` check and write-time `domain_warnings`)
416
+
417
+ (LLM reranking, deferred here originally, has since shipped as the opt-in
418
+ agent-as-judge `rerank_candidates` on `vault_search`: the server prepares the
419
+ fused candidate pool and the protocol; the calling agent is the judge.)
266
420
 
267
421
  Each is a clean increment on a surface that already works.
268
422
 
@@ -493,6 +647,36 @@ collection-pair > same-kind; no LLM): the court retrieves how this house has
493
647
  resolved similar disputes before, and whether a precedent applies stays the
494
648
  human's judgment. Memory grows case law.
495
649
 
650
+ ## The principal interview
651
+
652
+ The vault interrogates you. `daftari interview` assembles a question sheet
653
+ from what the vault already knows is unclear — open tensions (contested
654
+ first, longest-carried first), canonical accumulation docs past their TTL,
655
+ and `questions_raised` entries no document answers — deterministic and
656
+ LLM-free, like the circadian pass:
657
+
658
+ ```bash
659
+ # The sheet — read-only, priority-ordered
660
+ daftari interview --vault ./my-vault
661
+
662
+ # Sit for it. Answers are recorded VERBATIM; empty skips, 'q' ends.
663
+ daftari interview ask --vault ./my-vault
664
+ ```
665
+
666
+ Answered questions become a transcript document in an `interviews/`
667
+ collection — `tier: source` (your words, body immutable), `provenance:
668
+ direct`, no TTL (testimony doesn't expire) — committed like any other write,
669
+ with `sources` tracing each question back to the tension or document that
670
+ prompted it. The pattern is the companion interview on a living-summary
671
+ page: the compiler's highest-value move is not more compilation, it is
672
+ asking the principal about the unclear spots and folding the verbatim
673
+ answers back into the corpus.
674
+
675
+ Recording testimony resolves nothing. The transcript is *evidence*: the CLI
676
+ ends by pointing at `daftari court rule <id> --references <transcript>` for
677
+ tensions it touched, and at re-verified writes for stale docs. Operator-only,
678
+ like the court — no MCP tool, no access context.
679
+
496
680
  ## Belief archaeology
497
681
 
498
682
  Git is the version layer, so the vault can answer **"what did we believe on
@@ -12,6 +12,7 @@ export declare function canRead(role: RoleConfig | null, collection: string): bo
12
12
  export declare function canWrite(role: RoleConfig | null, collection: string): boolean;
13
13
  export declare function canPromote(role: RoleConfig | null): boolean;
14
14
  export declare function canRatify(role: RoleConfig | null): boolean;
15
+ export declare function isProposeOnly(role: RoleConfig | null): boolean;
15
16
  export declare function hasAnyRead(role: RoleConfig | null): boolean;
16
17
  export declare function filterByReadPermission<T extends {
17
18
  collection: string;
@@ -1 +1 @@
1
- {"version":3,"file":"rbac.d.ts","sourceRoot":"","sources":["../../src/access/rbac.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEpE,eAAO,MAAM,UAAU,UAAU,CAAC;AAClC,eAAO,MAAM,QAAQ,MAAM,CAAC;AAI5B,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;CACzB;AAKD,wBAAgB,aAAa,CAC3B,MAAM,EAAE,aAAa,EACrB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACf,aAAa,CAEf;AAID,wBAAgB,WAAW,CAAC,IAAI,SAAU,GAAG,aAAa,CAEzD;AAOD,wBAAgB,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAE5E;AAGD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAE7E;AAGD,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,OAAO,CAE3D;AAMD,wBAAgB,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,OAAO,CAE1D;AAID,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,OAAO,CAE3D;AAID,wBAAgB,sBAAsB,CAAC,CAAC,SAAS;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,EACrE,IAAI,EAAE,UAAU,GAAG,IAAI,EACvB,KAAK,EAAE,CAAC,EAAE,GACT,CAAC,EAAE,CAEL"}
1
+ {"version":3,"file":"rbac.d.ts","sourceRoot":"","sources":["../../src/access/rbac.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEpE,eAAO,MAAM,UAAU,UAAU,CAAC;AAClC,eAAO,MAAM,QAAQ,MAAM,CAAC;AAI5B,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;CACzB;AAKD,wBAAgB,aAAa,CAC3B,MAAM,EAAE,aAAa,EACrB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACf,aAAa,CAEf;AAID,wBAAgB,WAAW,CAAC,IAAI,SAAU,GAAG,aAAa,CAEzD;AAOD,wBAAgB,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAE5E;AAGD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAE7E;AAGD,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,OAAO,CAE3D;AAMD,wBAAgB,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,OAAO,CAE1D;AAMD,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,OAAO,CAE9D;AAID,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,OAAO,CAE3D;AAID,wBAAgB,sBAAsB,CAAC,CAAC,SAAS;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,EACrE,IAAI,EAAE,UAAU,GAAG,IAAI,EACvB,KAAK,EAAE,CAAC,EAAE,GACT,CAAC,EAAE,CAEL"}
@@ -42,6 +42,13 @@ export function canPromote(role) {
42
42
  export function canRatify(role) {
43
43
  return role?.ratify ?? false;
44
44
  }
45
+ // True if the role is propose-only (#235): its writes must land as staged
46
+ // `write` proposals, never as direct mutations. vault_write coerces; every
47
+ // other write tool denies. The write grant still scopes WHICH collections the
48
+ // role may propose into.
49
+ export function isProposeOnly(role) {
50
+ return role?.proposeOnly ?? false;
51
+ }
45
52
  // True if the role has read access to at least one collection. Curation tools
46
53
  // (lint, tension log, provenance) are open to anyone with any read grant.
47
54
  export function hasAnyRead(role) {
@@ -1 +1 @@
1
- {"version":3,"file":"rbac.js","sourceRoot":"","sources":["../../src/access/rbac.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,EAAE;AACF,6EAA6E;AAC7E,8EAA8E;AAC9E,2EAA2E;AAC3E,EAAE;AACF,8EAA8E;AAC9E,2EAA2E;AAC3E,0EAA0E;AAI1E,MAAM,CAAC,MAAM,UAAU,GAAG,OAAO,CAAC;AAClC,MAAM,CAAC,MAAM,QAAQ,GAAG,GAAG,CAAC;AAU5B,+EAA+E;AAC/E,6EAA6E;AAC7E,2BAA2B;AAC3B,MAAM,UAAU,aAAa,CAC3B,MAAqB,EACrB,IAAY,EACZ,QAAgB;IAEhB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC;AAClE,CAAC;AAED,2EAA2E;AAC3E,0BAA0B;AAC1B,MAAM,UAAU,WAAW,CAAC,IAAI,GAAG,OAAO;IACxC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACpD,CAAC;AAED,SAAS,OAAO,CAAC,IAAc,EAAE,UAAkB;IACjD,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;AAC9D,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,OAAO,CAAC,IAAuB,EAAE,UAAkB;IACjE,OAAO,IAAI,KAAK,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;AACzD,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,QAAQ,CAAC,IAAuB,EAAE,UAAkB;IAClE,OAAO,IAAI,KAAK,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;AAC1D,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,UAAU,CAAC,IAAuB;IAChD,OAAO,IAAI,EAAE,OAAO,IAAI,KAAK,CAAC;AAChC,CAAC;AAED,8EAA8E;AAC9E,4EAA4E;AAC5E,wEAAwE;AACxE,kDAAkD;AAClD,MAAM,UAAU,SAAS,CAAC,IAAuB;IAC/C,OAAO,IAAI,EAAE,MAAM,IAAI,KAAK,CAAC;AAC/B,CAAC;AAED,8EAA8E;AAC9E,0EAA0E;AAC1E,MAAM,UAAU,UAAU,CAAC,IAAuB;IAChD,OAAO,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;AAC/C,CAAC;AAED,8EAA8E;AAC9E,wBAAwB;AACxB,MAAM,UAAU,sBAAsB,CACpC,IAAuB,EACvB,KAAU;IAEV,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;AAChE,CAAC"}
1
+ {"version":3,"file":"rbac.js","sourceRoot":"","sources":["../../src/access/rbac.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,EAAE;AACF,6EAA6E;AAC7E,8EAA8E;AAC9E,2EAA2E;AAC3E,EAAE;AACF,8EAA8E;AAC9E,2EAA2E;AAC3E,0EAA0E;AAI1E,MAAM,CAAC,MAAM,UAAU,GAAG,OAAO,CAAC;AAClC,MAAM,CAAC,MAAM,QAAQ,GAAG,GAAG,CAAC;AAU5B,+EAA+E;AAC/E,6EAA6E;AAC7E,2BAA2B;AAC3B,MAAM,UAAU,aAAa,CAC3B,MAAqB,EACrB,IAAY,EACZ,QAAgB;IAEhB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC;AAClE,CAAC;AAED,2EAA2E;AAC3E,0BAA0B;AAC1B,MAAM,UAAU,WAAW,CAAC,IAAI,GAAG,OAAO;IACxC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACpD,CAAC;AAED,SAAS,OAAO,CAAC,IAAc,EAAE,UAAkB;IACjD,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;AAC9D,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,OAAO,CAAC,IAAuB,EAAE,UAAkB;IACjE,OAAO,IAAI,KAAK,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;AACzD,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,QAAQ,CAAC,IAAuB,EAAE,UAAkB;IAClE,OAAO,IAAI,KAAK,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;AAC1D,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,UAAU,CAAC,IAAuB;IAChD,OAAO,IAAI,EAAE,OAAO,IAAI,KAAK,CAAC;AAChC,CAAC;AAED,8EAA8E;AAC9E,4EAA4E;AAC5E,wEAAwE;AACxE,kDAAkD;AAClD,MAAM,UAAU,SAAS,CAAC,IAAuB;IAC/C,OAAO,IAAI,EAAE,MAAM,IAAI,KAAK,CAAC;AAC/B,CAAC;AAED,0EAA0E;AAC1E,2EAA2E;AAC3E,8EAA8E;AAC9E,yBAAyB;AACzB,MAAM,UAAU,aAAa,CAAC,IAAuB;IACnD,OAAO,IAAI,EAAE,WAAW,IAAI,KAAK,CAAC;AACpC,CAAC;AAED,8EAA8E;AAC9E,0EAA0E;AAC1E,MAAM,UAAU,UAAU,CAAC,IAAuB;IAChD,OAAO,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;AAC/C,CAAC;AAED,8EAA8E;AAC9E,wBAAwB;AACxB,MAAM,UAAU,sBAAsB,CACpC,IAAuB,EACvB,KAAU;IAEV,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;AAChE,CAAC"}
@@ -1,3 +1,3 @@
1
1
  import type { BrokenRefFinding, LinkEdge, RepoSnapshot } from "../types.js";
2
- export declare function checkBrokenRefs(snapshots: RepoSnapshot[], edges: LinkEdge[]): BrokenRefFinding[];
2
+ export declare function checkBrokenRefs(snapshots: RepoSnapshot[], edges: LinkEdge[], existsWithin?: (rootAbs: string, targetAbs: string) => boolean): BrokenRefFinding[];
3
3
  //# sourceMappingURL=broken_refs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"broken_refs.d.ts","sourceRoot":"","sources":["../../../src/audit/checks/broken_refs.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE5E,wBAAgB,eAAe,CAAC,SAAS,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,gBAAgB,EAAE,CA0DhG"}
1
+ {"version":3,"file":"broken_refs.d.ts","sourceRoot":"","sources":["../../../src/audit/checks/broken_refs.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAS5E,wBAAgB,eAAe,CAC7B,SAAS,EAAE,YAAY,EAAE,EACzB,KAAK,EAAE,QAAQ,EAAE,EAIjB,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,GAC7D,gBAAgB,EAAE,CAiGpB"}
@@ -1,32 +1,72 @@
1
1
  // src/audit/checks/broken_refs.ts
2
2
  // Pure function: given snapshots and classified edges, return every broken
3
- // reference as a BrokenRefFinding. No throws. No classes.
3
+ // reference as a BrokenRefFinding. No throws. No classes. Disk access enters
4
+ // ONLY through the injected `existsWithin` oracle — the orchestrator passes
5
+ // collect.ts's symlink-safe implementation; tests pass a fake; omitting it
6
+ // reproduces the index-only behavior. The oracle receives the CONTAINMENT
7
+ // ROOT alongside the target because existence and confinement must be
8
+ // decided together: a lexical check here plus a bare exists() there would
9
+ // let a committed symlink (repo/escape -> /) walk the probe back out.
4
10
  //
5
11
  // Resolution logic:
6
12
  // 1. Look up target repo by name; if not found → missing_file.
7
13
  // 2. Look up targetPath in that repo's docs Map.
8
14
  // 3. If not found and targetPath lacks a ".md" extension, try targetPath +
9
15
  // ".md" (spec §7 bare-path fallback).
10
- // 4. If still not found missing_file.
11
- // 5. If found and targetAnchor is set: check DocSnapshot.headings. If absent
16
+ // 4. Still not found, edge escaped every configured repo (outOfScope):
17
+ // existing on disk out_of_scope_target (the audit can't vouch for it,
18
+ // but the link is not broken — #133); absent → missing_file.
19
+ // 5. Still not found, in-scope: existing on disk → NOT a finding — the doc
20
+ // index only holds glob-matched markdown, and references to real assets
21
+ // (.png, .pdf, .dagitty, …) are legitimate (#132). Anchors into such
22
+ // files are unverifiable and deliberately not flagged.
23
+ // 6. Otherwise → missing_file.
24
+ // 7. If found and targetAnchor is set: check DocSnapshot.headings. If absent
12
25
  // → missing_anchor.
13
- export function checkBrokenRefs(snapshots, edges) {
26
+ import { isAbsolute, relative as nodeRelative, resolve as nodeResolve } from "node:path";
27
+ // True iff `abs` sits at or under `root` (lexical containment; both sides are
28
+ // already-resolved absolute paths).
29
+ function within(root, abs) {
30
+ const rel = nodeRelative(root, abs);
31
+ return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
32
+ }
33
+ export function checkBrokenRefs(snapshots, edges,
34
+ // Must return true only when targetAbs exists on disk AND its REAL
35
+ // (symlink-resolved) location sits under rootAbs. See
36
+ // collect.ts#symlinkSafeExistsWithin for the production implementation.
37
+ existsWithin) {
14
38
  // Index snapshots by repo name for O(1) lookup.
15
39
  const byRepo = new Map();
16
40
  for (const snap of snapshots) {
17
41
  byRepo.set(snap.config.name, snap);
18
42
  }
43
+ // The existence oracle is CONFINED (security review on #255): probing an
44
+ // arbitrary resolvedAbs would let anyone who can author a doc turn the
45
+ // audit report into a host-filesystem existence probe
46
+ // ([x](../../../../etc/shadow) → out_of_scope_target vs missing_file).
47
+ // #133's actual case is UNAUDITED SIBLINGS, so out-of-scope probes are
48
+ // allowed only under the parent directories of the configured repo roots;
49
+ // anything further out is reported missing_file without ever touching
50
+ // disk — exactly the pre-oracle behavior. A repo mounted directly under
51
+ // the filesystem root gets NO sibling scope: its "parent" would be the
52
+ // entire filesystem and the confinement would collapse to none, so
53
+ // out-of-scope refs from such a repo stay unprobed.
54
+ const siblingScopes = snapshots.flatMap((s) => {
55
+ const parent = nodeResolve(s.config.path, "..");
56
+ return nodeResolve(parent, "..") === parent ? [] : [parent];
57
+ });
19
58
  const findings = [];
20
59
  for (const edge of edges) {
60
+ const finding = (kind, path) => ({
61
+ kind,
62
+ source: { repo: edge.sourceRepo, path: edge.sourcePath },
63
+ target: { repo: edge.targetRepo, path, anchor: edge.targetAnchor },
64
+ rawHref: edge.rawHref,
65
+ });
21
66
  const targetSnap = byRepo.get(edge.targetRepo);
22
67
  if (!targetSnap) {
23
68
  // Target repo itself is unknown — treat as missing file.
24
- findings.push({
25
- kind: "missing_file",
26
- source: { repo: edge.sourceRepo, path: edge.sourcePath },
27
- target: { repo: edge.targetRepo, path: edge.targetPath, anchor: edge.targetAnchor },
28
- rawHref: edge.rawHref,
29
- });
69
+ findings.push(finding("missing_file", edge.targetPath));
30
70
  continue;
31
71
  }
32
72
  // Resolve the target doc: exact match, then .md fallback.
@@ -41,24 +81,39 @@ export function checkBrokenRefs(snapshots, edges) {
41
81
  }
42
82
  }
43
83
  if (resolvedPath === null) {
44
- findings.push({
45
- kind: "missing_file",
46
- source: { repo: edge.sourceRepo, path: edge.sourcePath },
47
- target: { repo: edge.targetRepo, path: edge.targetPath, anchor: edge.targetAnchor },
48
- rawHref: edge.rawHref,
49
- });
84
+ if (edge.outOfScope) {
85
+ // The href escaped every audited repo (#133). A file that exists out
86
+ // there is not a broken link — but it stays VISIBLE with its own
87
+ // kind, never silently passed: the audit cannot vouch for a target
88
+ // it does not scan. The probe runs only inside the sibling scopes
89
+ // (see above); a target beyond them is missing_file, unprobed.
90
+ const resolvedAbs = edge.resolvedAbs;
91
+ // Lexical pre-filter picks the candidate scope without touching
92
+ // disk; the oracle then re-verifies containment on REAL paths.
93
+ const scope = resolvedAbs !== undefined ? siblingScopes.find((s) => within(s, resolvedAbs)) : undefined;
94
+ findings.push(finding(resolvedAbs !== undefined && scope !== undefined && existsWithin?.(scope, resolvedAbs)
95
+ ? "out_of_scope_target"
96
+ : "missing_file", edge.targetPath));
97
+ continue;
98
+ }
99
+ // In-scope miss: the docs map only holds glob-matched markdown, but a
100
+ // reference to a real on-disk asset is legitimate (#132) — one
101
+ // existence probe, no finding. Anchors into assets are unverifiable.
102
+ // The probe is confined to the target repo root: a URL-derived
103
+ // targetPath can carry ../ segments, and escaping paths must not be
104
+ // probed (same oracle concern as above).
105
+ const abs = nodeResolve(targetSnap.config.path, edge.targetPath);
106
+ if (within(targetSnap.config.path, abs) && existsWithin?.(targetSnap.config.path, abs)) {
107
+ continue;
108
+ }
109
+ findings.push(finding("missing_file", edge.targetPath));
50
110
  continue;
51
111
  }
52
112
  // File found. Check anchor if present.
53
113
  if (edge.targetAnchor !== null) {
54
114
  const doc = targetSnap.docs.get(resolvedPath);
55
115
  if (doc && !doc.headings.has(edge.targetAnchor)) {
56
- findings.push({
57
- kind: "missing_anchor",
58
- source: { repo: edge.sourceRepo, path: edge.sourcePath },
59
- target: { repo: edge.targetRepo, path: resolvedPath, anchor: edge.targetAnchor },
60
- rawHref: edge.rawHref,
61
- });
116
+ findings.push(finding("missing_anchor", resolvedPath));
62
117
  }
63
118
  }
64
119
  }
@@ -1 +1 @@
1
- {"version":3,"file":"broken_refs.js","sourceRoot":"","sources":["../../../src/audit/checks/broken_refs.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"broken_refs.js","sourceRoot":"","sources":["../../../src/audit/checks/broken_refs.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { type Result } from "../frontmatter/types.js";
2
2
  import type { AuditConfig, AuditError, RepoSnapshot } from "./types.js";
3
+ export declare function symlinkSafeExistsWithin(rootAbs: string, targetAbs: string): boolean;
3
4
  export declare function collectRepos(config: AuditConfig): Promise<Result<RepoSnapshot[], AuditError>>;
4
5
  //# sourceMappingURL=collect.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"collect.d.ts","sourceRoot":"","sources":["../../src/audit/collect.ts"],"names":[],"mappings":"AAWA,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAE/D,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAA2B,YAAY,EAAE,MAAM,YAAY,CAAC;AAqKjG,wBAAsB,YAAY,CAChC,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,EAAE,UAAU,CAAC,CAAC,CAa7C"}
1
+ {"version":3,"file":"collect.d.ts","sourceRoot":"","sources":["../../src/audit/collect.ts"],"names":[],"mappings":"AAWA,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAE/D,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAA2B,YAAY,EAAE,MAAM,YAAY,CAAC;AAYjG,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CASnF;AAoKD,wBAAsB,YAAY,CAChC,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,EAAE,UAAU,CAAC,CAAC,CAa7C"}