@gmickel/gno 1.27.1 → 1.29.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 (195) hide show
  1. package/README.md +46 -20
  2. package/assets/skill/SKILL.md +59 -1
  3. package/browser-extension/artifacts/gno-browser-clipper-v1.29.0.zip +0 -0
  4. package/browser-extension/artifacts/gno-browser-clipper-v1.29.0.zip.sha256 +1 -0
  5. package/browser-extension/dist/chunk-b2zm0jjd.js +50 -0
  6. package/browser-extension/dist/manifest.json +1 -1
  7. package/browser-extension/dist/preview.html +1 -1
  8. package/browser-extension/dist/service-worker.js +6 -6
  9. package/package.json +2 -2
  10. package/spec/cli.md +82 -5
  11. package/spec/db/schema.sql +31 -0
  12. package/spec/mcp.md +71 -0
  13. package/spec/output-schemas/ask.schema.json +156 -1
  14. package/spec/output-schemas/browser-clip-preview.schema.json +30 -0
  15. package/spec/output-schemas/collection-egress-check.schema.json +91 -0
  16. package/spec/output-schemas/collection-egress-policy-set.schema.json +56 -0
  17. package/spec/output-schemas/collection-egress-policy.schema.json +41 -0
  18. package/spec/output-schemas/context-capsule-v1.schema.json +183 -3
  19. package/spec/output-schemas/context-capsule-verification.schema.json +1 -1
  20. package/spec/output-schemas/egress-audit-management.schema.json +88 -0
  21. package/spec/output-schemas/get.schema.json +94 -0
  22. package/spec/output-schemas/mcp-http-error.schema.json +113 -2
  23. package/spec/output-schemas/mcp-job-status.schema.json +28 -0
  24. package/spec/output-schemas/multi-get.schema.json +128 -0
  25. package/spec/output-schemas/publish-artifact.schema.json +32 -0
  26. package/spec/output-schemas/record-import.schema.json +223 -0
  27. package/spec/output-schemas/retrieval-trace-common.schema.json +31 -0
  28. package/spec/output-schemas/retrieval-trace-export.schema.json +3 -0
  29. package/spec/output-schemas/retrieval-trace-qrels.schema.json +3 -0
  30. package/spec/output-schemas/retrieval-trace-summary.schema.json +4 -0
  31. package/spec/output-schemas/search-result.schema.json +94 -0
  32. package/spec/output-schemas/search-results.schema.json +155 -0
  33. package/spec/project-profile.schema.json +66 -0
  34. package/src/app/context-format.ts +1 -1
  35. package/src/app/context-runtime-contract.ts +16 -7
  36. package/src/cli/commands/ask.ts +7 -0
  37. package/src/cli/commands/collection/index.ts +5 -0
  38. package/src/cli/commands/collection/policy.ts +126 -0
  39. package/src/cli/commands/context-build.ts +5 -0
  40. package/src/cli/commands/daemon.ts +45 -3
  41. package/src/cli/commands/egress-audit.ts +68 -0
  42. package/src/cli/commands/embed.ts +2 -0
  43. package/src/cli/commands/get.ts +12 -2
  44. package/src/cli/commands/index-cmd.ts +13 -0
  45. package/src/cli/commands/multi-get.ts +9 -2
  46. package/src/cli/commands/query.ts +12 -0
  47. package/src/cli/commands/replay.ts +13 -3
  48. package/src/cli/commands/shared.ts +28 -0
  49. package/src/cli/commands/update.ts +5 -0
  50. package/src/cli/commands/vsearch.ts +3 -1
  51. package/src/cli/program.ts +127 -0
  52. package/src/config/index.ts +9 -0
  53. package/src/config/project-profile.ts +2 -0
  54. package/src/config/types.ts +66 -0
  55. package/src/converters/adapters/browser-export/adapter.ts +199 -0
  56. package/src/converters/adapters/browser-export/formats.ts +358 -0
  57. package/src/converters/adapters/email/adapter.ts +429 -0
  58. package/src/converters/adapters/email/html.ts +162 -0
  59. package/src/converters/adapters/email/mime.ts +454 -0
  60. package/src/converters/adapters/email/parameters.ts +77 -0
  61. package/src/converters/adapters/ical/adapter.ts +475 -0
  62. package/src/converters/adapters/ical/recurrence.ts +186 -0
  63. package/src/converters/adapters/jsonl/adapter.ts +238 -0
  64. package/src/converters/adapters/jsonl/config.ts +105 -0
  65. package/src/converters/adapters/shared/html-text.ts +165 -0
  66. package/src/converters/adapters/shared/record-utils.ts +79 -0
  67. package/src/converters/adapters/shared/utf8-lines.ts +141 -0
  68. package/src/converters/adapters/transcript/adapter.ts +295 -0
  69. package/src/converters/adapters/transcript/json.ts +184 -0
  70. package/src/converters/adapters/transcript/model.ts +171 -0
  71. package/src/converters/adapters/transcript/text.ts +58 -0
  72. package/src/converters/adapters/transcript/timed.ts +152 -0
  73. package/src/converters/index.ts +11 -1
  74. package/src/converters/mime.ts +8 -0
  75. package/src/converters/pipeline.ts +15 -1
  76. package/src/converters/registry.ts +37 -1
  77. package/src/converters/types.ts +136 -0
  78. package/src/core/browser-clip.ts +16 -1
  79. package/src/core/collection-egress-policy-projection.ts +28 -0
  80. package/src/core/collection-egress-policy-service.ts +443 -0
  81. package/src/core/collection-egress-policy-validation.ts +242 -0
  82. package/src/core/config-mutation.ts +31 -19
  83. package/src/core/context-capsule-schema.ts +198 -1
  84. package/src/core/context-capsule-validation.ts +4 -4
  85. package/src/core/context-capsule-verification.ts +5 -1
  86. package/src/core/context-capsule.ts +177 -112
  87. package/src/core/context-evidence.ts +93 -15
  88. package/src/core/destination-classifier.ts +402 -0
  89. package/src/core/document-capabilities.ts +12 -0
  90. package/src/core/egress-audit.ts +239 -0
  91. package/src/core/egress-authorization.ts +50 -0
  92. package/src/core/egress-enforcement.ts +264 -0
  93. package/src/core/egress-policy.ts +440 -0
  94. package/src/core/egress-provenance.ts +336 -0
  95. package/src/core/job-manager.ts +37 -0
  96. package/src/core/network-boundary-inventory.ts +261 -0
  97. package/src/core/project-profile-apply-state.ts +5 -0
  98. package/src/core/project-profile.ts +4 -0
  99. package/src/core/record-metadata.ts +49 -0
  100. package/src/core/retrieval-qrels.ts +6 -0
  101. package/src/core/retrieval-replay.ts +18 -4
  102. package/src/core/retrieval-trace-export.ts +23 -4
  103. package/src/core/retrieval-trace-management-helpers.ts +1 -0
  104. package/src/core/retrieval-trace-management-types.ts +3 -0
  105. package/src/core/retrieval-trace-management.ts +9 -1
  106. package/src/core/retrieval-trace-session.ts +94 -0
  107. package/src/core/retrieval-trace.ts +22 -0
  108. package/src/ingestion/record-adapter-canonical.ts +433 -0
  109. package/src/ingestion/record-adapter.ts +448 -0
  110. package/src/ingestion/record-container.ts +704 -0
  111. package/src/ingestion/record-path.ts +20 -0
  112. package/src/ingestion/record-sync.ts +70 -0
  113. package/src/ingestion/sync.ts +243 -36
  114. package/src/ingestion/types.ts +71 -1
  115. package/src/ingestion/walker.ts +31 -11
  116. package/src/llm/errors.ts +10 -0
  117. package/src/llm/http-inference.ts +175 -0
  118. package/src/llm/http-policy.ts +537 -0
  119. package/src/llm/httpEmbedding.ts +47 -28
  120. package/src/llm/httpGeneration.ts +31 -18
  121. package/src/llm/httpRerank.ts +30 -18
  122. package/src/llm/index.ts +1 -0
  123. package/src/llm/nodeLlamaCpp/adapter.ts +55 -20
  124. package/src/llm/pinned-http-connection.ts +177 -0
  125. package/src/mcp/context.ts +53 -1
  126. package/src/mcp/http-egress.ts +220 -0
  127. package/src/mcp/http-security.ts +32 -21
  128. package/src/mcp/http-session.ts +13 -2
  129. package/src/mcp/http-transport.ts +96 -6
  130. package/src/mcp/sync-egress.ts +24 -0
  131. package/src/mcp/tools/add-collection.ts +4 -0
  132. package/src/mcp/tools/ask.ts +1 -1
  133. package/src/mcp/tools/context.ts +9 -2
  134. package/src/mcp/tools/egress.ts +247 -0
  135. package/src/mcp/tools/embed.ts +1 -0
  136. package/src/mcp/tools/get.ts +10 -2
  137. package/src/mcp/tools/index-cmd.ts +7 -0
  138. package/src/mcp/tools/index.ts +98 -0
  139. package/src/mcp/tools/multi-get.ts +9 -2
  140. package/src/mcp/tools/query.ts +10 -0
  141. package/src/mcp/tools/remove-collection.ts +4 -0
  142. package/src/mcp/tools/sync.ts +6 -0
  143. package/src/mcp/tools/trace.ts +7 -1
  144. package/src/mcp/tools/vsearch.ts +1 -0
  145. package/src/mcp/tools/workspace-write.ts +2 -0
  146. package/src/pipeline/egress-lineage.ts +124 -0
  147. package/src/pipeline/filters.ts +1 -1
  148. package/src/pipeline/graph-retrieval.ts +7 -4
  149. package/src/pipeline/hybrid.ts +21 -4
  150. package/src/pipeline/result-context.ts +12 -4
  151. package/src/pipeline/search.ts +23 -4
  152. package/src/pipeline/types.ts +6 -0
  153. package/src/pipeline/vsearch.ts +67 -28
  154. package/src/publish/artifact.ts +31 -3
  155. package/src/publish/export-service.ts +25 -0
  156. package/src/sdk/client.ts +119 -2
  157. package/src/sdk/documents.ts +13 -5
  158. package/src/sdk/embed.ts +1 -0
  159. package/src/sdk/types.ts +33 -0
  160. package/src/serve/browse-tree.ts +4 -2
  161. package/src/serve/clipper-capture.ts +5 -1
  162. package/src/serve/closed-json.ts +61 -0
  163. package/src/serve/config-sync.ts +4 -1
  164. package/src/serve/context.ts +1 -0
  165. package/src/serve/doc-events.ts +110 -39
  166. package/src/serve/public/components/CollectionEgressCheckPanel.tsx +411 -0
  167. package/src/serve/public/components/CollectionEgressPolicyEditor.tsx +78 -0
  168. package/src/serve/public/components/CollectionModelDialog.tsx +93 -9
  169. package/src/serve/public/components/EgressAuditPanel.tsx +278 -0
  170. package/src/serve/public/globals.built.css +1 -1
  171. package/src/serve/public/pages/Collections.tsx +3 -0
  172. package/src/serve/resident-request.ts +80 -3
  173. package/src/serve/resident-runtime.ts +63 -1
  174. package/src/serve/routes/api.ts +237 -65
  175. package/src/serve/routes/clipper.ts +64 -1
  176. package/src/serve/routes/mcp.ts +6 -0
  177. package/src/serve/routes/traces.ts +27 -4
  178. package/src/serve/server.ts +118 -7
  179. package/src/store/migrations/022-record-export-lineage.ts +42 -0
  180. package/src/store/migrations/023-collection-egress-policy.ts +61 -0
  181. package/src/store/migrations/024-egress-derived-lineage.ts +121 -0
  182. package/src/store/migrations/025-collection-egress-policy-revision.ts +40 -0
  183. package/src/store/migrations/index.ts +8 -0
  184. package/src/store/retrieval-trace-codec.ts +15 -0
  185. package/src/store/sqlite/adapter.ts +284 -14
  186. package/src/store/sqlite/change-journal-store.ts +41 -2
  187. package/src/store/sqlite/egress-audit-store.ts +485 -0
  188. package/src/store/sqlite/retrieval-trace-management-store.ts +47 -6
  189. package/src/store/sqlite/retrieval-trace-retention.ts +2 -0
  190. package/src/store/sqlite/retrieval-trace-rows.ts +8 -1
  191. package/src/store/sqlite/retrieval-trace-store.ts +104 -5
  192. package/src/store/types.ts +166 -1
  193. package/browser-extension/artifacts/gno-browser-clipper-v1.27.1.zip +0 -0
  194. package/browser-extension/artifacts/gno-browser-clipper-v1.27.1.zip.sha256 +0 -1
  195. package/browser-extension/dist/chunk-vn5f663b.js +0 -50
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gmickel/gno",
3
- "version": "1.27.1",
3
+ "version": "1.29.0",
4
4
  "description": "Local semantic search for your documents. Index Markdown, PDF, and Office files with hybrid BM25 + vector search.",
5
5
  "keywords": [
6
6
  "embeddings",
@@ -220,7 +220,7 @@
220
220
  "playwright": "1.58.2",
221
221
  "pptxgenjs": "4.0.1",
222
222
  "ultracite": "7.1.5",
223
- "vitest": "4.0.18",
223
+ "vitest": "4.1.0",
224
224
  "web-tree-sitter": "0.26.8"
225
225
  },
226
226
  "peerDependencies": {
package/spec/cli.md CHANGED
@@ -73,8 +73,8 @@ equivalent files fail closed as ambiguous.
73
73
  | collection list | yes | no | no | yes | no | terminal |
74
74
  | collection remove | no | no | no | no | no | terminal |
75
75
  | collection rename | no | no | no | no | no | terminal |
76
- | update | no | no | no | no | no | terminal |
77
- | index | no | no | no | no | no | terminal |
76
+ | update | yes | no | no | no | no | terminal |
77
+ | index | yes | no | no | no | no | terminal |
78
78
  | embed | no | no | no | no | no | terminal |
79
79
  | search | yes | yes | yes | yes | yes | terminal |
80
80
  | vsearch | yes | yes | yes | yes | yes | terminal |
@@ -672,6 +672,44 @@ gno collection clear-embeddings <name> [--all] [--json]
672
672
 
673
673
  ---
674
674
 
675
+ ### gno collection policy
676
+
677
+ Inspect, change, or explain one collection-owned egress boundary.
678
+
679
+ ```bash
680
+ gno collection policy get <name>
681
+ gno collection policy set <name> <local_only|lan|remote> [--confirm-relaxation <current-revision>]
682
+ gno collection policy check --action <action> --destination <zone> --content-class <class> [-c <name>...] [--authenticated] [--authorized] [--partial] [--explain-egress]
683
+ ```
684
+
685
+ `get` returns the configured value, effective value, provenance source, and a
686
+ durable monotonic revision plus a diagnostic version fingerprint. Tightening is
687
+ immediate. Relaxation requires `--confirm-relaxation` with the exact current
688
+ numeric revision returned by `get`; stale, replayed, cross-collection, and
689
+ cross-target confirmation fails without changing config or the index
690
+ projection.
691
+ `check` and `--explain-egress` perform no action. They return the same
692
+ content-free decision, lineage, partial disclosure, audit metadata, and
693
+ remediation contract.
694
+
695
+ ### gno egress-audit
696
+
697
+ Manage local content-free policy decision receipts:
698
+
699
+ ```bash
700
+ gno egress-audit list [--limit <n>] [--cursor <cursor>]
701
+ gno egress-audit show <audit-id>
702
+ gno egress-audit status
703
+ gno egress-audit delete <audit-id>
704
+ gno egress-audit purge
705
+ ```
706
+
707
+ Pages are newest-first. Delete/purge report truthful SQLite physical cleanup
708
+ status. These local inspection and deletion commands never require a relaxed
709
+ collection policy.
710
+
711
+ ---
712
+
675
713
  ### gno embed
676
714
 
677
715
  Generate embeddings for indexed chunks.
@@ -772,13 +810,14 @@ Sync files from disk into the index (ingestion without embedding).
772
810
  **Synopsis:**
773
811
 
774
812
  ```bash
775
- gno update [--git-pull]
813
+ gno update [--git-pull] [--json]
776
814
  ```
777
815
 
778
816
  **Options:**
779
817
  | Option | Type | Description |
780
818
  |--------|------|-------------|
781
819
  | `--git-pull` | boolean | Run `git pull` in git repositories before scanning |
820
+ | `--json` | boolean | Emit the complete deterministic sync result on stdout |
782
821
 
783
822
  **Behavior:**
784
823
 
@@ -789,6 +828,13 @@ gno update [--git-pull]
789
828
  5. Update FTS index
790
829
  6. Mark missing files as inactive
791
830
 
831
+ **JSON output:** The top-level value is the complete sync result. Each entry in
832
+ `collections[].files[]` may contain a `recordImport` receipt conforming to
833
+ [`record-import@1.0`](./output-schemas/record-import.schema.json). Receipt items
834
+ are sorted deterministically and capped at 1,000; `itemsTruncated` reports
835
+ omitted actions. Partial snapshots expose `warnings[]` even when no individual
836
+ record failure was reported. Human progress and diagnostics remain on stderr.
837
+
792
838
  **Exit Codes:**
793
839
 
794
840
  - 0: Success (conversion warnings do not affect exit code)
@@ -803,7 +849,7 @@ Build or update the index end-to-end (update + embed).
803
849
  **Synopsis:**
804
850
 
805
851
  ```bash
806
- gno index [--collection <name>] [--no-embed] [--models-pull] [--git-pull] [--yes]
852
+ gno index [collection] [--no-embed] [--models-pull] [--git-pull] [--json] [--yes]
807
853
  ```
808
854
 
809
855
  **Options:**
@@ -813,6 +859,7 @@ gno index [--collection <name>] [--no-embed] [--models-pull] [--git-pull] [--yes
813
859
  | `--no-embed` | boolean | Run ingestion only, skip embedding |
814
860
  | `--models-pull` | boolean | Download models if missing (prompts unless `--yes`) |
815
861
  | `--git-pull` | boolean | Run `git pull` in git repositories |
862
+ | `--json` | boolean | Emit the complete deterministic sync and embedding result on stdout |
816
863
  | `--yes` | boolean | Accept defaults, no prompts |
817
864
 
818
865
  **Behavior:**
@@ -820,6 +867,13 @@ gno index [--collection <name>] [--no-embed] [--models-pull] [--git-pull] [--yes
820
867
  - Runs `update` then `embed` by default
821
868
  - With `--no-embed`, runs `update` only
822
869
 
870
+ **JSON output:** Emits `{ syncResult, embedSkipped, embedResult? }`.
871
+ `syncResult.collections[].files[].recordImport`, when present, conforms to
872
+ [`record-import@1.0`](./output-schemas/record-import.schema.json) with the same
873
+ deterministic ordering, bounds, truncation disclosure, and partial-snapshot
874
+ warnings as `gno update --json`. Human progress and diagnostics remain on
875
+ stderr.
876
+
823
877
  **Exit Codes:**
824
878
 
825
879
  - 0: Success
@@ -1034,6 +1088,27 @@ broad-to-specific path-prefix order. The field is absent when no scope matches;
1034
1088
  `uri` and `docid` remain the exact source identity. The same contract applies to
1035
1089
  `vsearch`, `query`, and the `results` array returned by `ask`.
1036
1090
 
1091
+ Logical documents created from file/export adapters additionally include a
1092
+ bounded `record` object with their opaque record key, exact export locator,
1093
+ people/dates, thread/event/session identity, attachment inventory, and anchors.
1094
+ `source.relPath` names the real export container; `uri` and `docid` address the
1095
+ individual virtual document. The closed `record.adapter` identity contains the
1096
+ adapter ID, version, and configuration fingerprint. `get` and `multi-get`
1097
+ preserve the same fields.
1098
+
1099
+ File/export adapter iteration has a 60-second deadline in addition to the byte,
1100
+ record, metadata, and total-character caps. A partial import that retains valid
1101
+ siblings exits successfully but is never silent: terminal output includes the
1102
+ file, bounded warning count, and snapshot state; `--verbose` adds stable failure
1103
+ codes, bounded source locators, retryability, and redacted messages.
1104
+ `gno update --json`, `gno index --json`, and internal and SDK-facing sync
1105
+ results include a `recordImport` receipt conforming to
1106
+ [`record-import@1.0`](./output-schemas/record-import.schema.json), with adapter
1107
+ identity, configuration fingerprint, snapshot authority, cap state, bytes read,
1108
+ per-action identity/provenance/attachment receipts, truncation disclosure,
1109
+ warnings, and bounded failures. `.gno/records/` is a reserved virtual
1110
+ namespace and is never walked as physical collection content.
1111
+
1037
1112
  **Exit Codes:**
1038
1113
 
1039
1114
  - 0: Success (including zero results)
@@ -3236,7 +3311,9 @@ is blocked.
3236
3311
  - Starts a headless `/mcp` Streamable HTTP listener; it does not serve the Web UI
3237
3312
  - Exposes the same safe REST lifecycle snapshot at `/api/resident/status`;
3238
3313
  resident-aware app status at `/api/status` is loopback-only because it
3239
- includes local index and configuration details
3314
+ includes local index and configuration details. Non-loopback resident status
3315
+ intersects exact Host/Origin and bearer authentication with the current
3316
+ peer zone and all participating collection policies before returning metadata
3240
3317
  - On `--detach`: forks a detached child with stdio redirected to `--log-file`, writes pid-file JSON including the MCP gateway `port`, prints `{pid}` on stdout, exits 0
3241
3318
  - On `--status`: output matches the [process-status schema](./output-schemas/process-status.schema.json), including the MCP gateway port and a best-effort copy of the live redacted resident snapshot
3242
3319
  - On `--stop`: SIGTERM → 10s poll → SIGKILL → 2s poll; the daemon's own signal handler unlinks the pid-file, `--stop` unlinks as fallback
@@ -49,6 +49,25 @@ CREATE TABLE IF NOT EXISTS collections (
49
49
  exclude TEXT, -- JSON array of patterns
50
50
  update_cmd TEXT,
51
51
  language_hint TEXT, -- BCP-47 or NULL
52
+ -- Migration 023 backfills existing rows as local_only/legacy_default.
53
+ -- Config sync without an explicit value remains local_only/config_default;
54
+ -- only explicit config may persist lan or remote.
55
+ egress_policy TEXT NOT NULL DEFAULT 'local_only'
56
+ CHECK (egress_policy IN ('local_only', 'lan', 'remote')),
57
+ egress_policy_source TEXT NOT NULL DEFAULT 'legacy_default'
58
+ CHECK (
59
+ egress_policy_source IN (
60
+ 'explicit',
61
+ 'config_default',
62
+ 'legacy_default'
63
+ )
64
+ )
65
+ CHECK (
66
+ egress_policy_source = 'explicit'
67
+ OR egress_policy = 'local_only'
68
+ ),
69
+ egress_policy_revision INTEGER NOT NULL DEFAULT 0
70
+ CHECK (egress_policy_revision >= 0),
52
71
  synced_at TEXT NOT NULL DEFAULT (datetime('now'))
53
72
  );
54
73
 
@@ -92,6 +111,12 @@ CREATE TABLE IF NOT EXISTS documents (
92
111
  converter_version TEXT,
93
112
  language_hint TEXT, -- BCP-47 or NULL
94
113
  content_type_source TEXT, -- frontmatter | rules | default | extension | null
114
+ record_key TEXT, -- opaque stable logical-record identity
115
+ record_source_path TEXT, -- collection-relative export/container path
116
+ record_source_locator TEXT, -- bounded exact record/cue/message/event locator
117
+ record_metadata TEXT, -- bounded canonical JSON record metadata
118
+ record_anchors TEXT, -- bounded canonical JSON exact anchors
119
+ record_adapter_fingerprint TEXT, -- config-bound adapter identity for re-import invalidation
95
120
 
96
121
  -- Status
97
122
  active INTEGER NOT NULL DEFAULT 1,
@@ -116,6 +141,12 @@ CREATE INDEX IF NOT EXISTS idx_documents_active ON documents(active);
116
141
  CREATE INDEX IF NOT EXISTS idx_documents_mirror_hash ON documents(mirror_hash);
117
142
  CREATE INDEX IF NOT EXISTS idx_documents_docid ON documents(docid);
118
143
  CREATE INDEX IF NOT EXISTS idx_documents_uri ON documents(uri);
144
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_documents_record_key
145
+ ON documents(collection, record_source_path, record_key)
146
+ WHERE record_source_path IS NOT NULL AND record_key IS NOT NULL;
147
+ CREATE INDEX IF NOT EXISTS idx_documents_record_source_path
148
+ ON documents(collection, record_source_path)
149
+ WHERE record_source_path IS NOT NULL;
119
150
 
120
151
  -- ─────────────────────────────────────────────────────────────────────────────
121
152
  -- Content (content-addressed markdown mirrors)
package/spec/mcp.md CHANGED
@@ -98,6 +98,26 @@ HTTP MCP remains read-only unless `gateway.enableWrite: true` or
98
98
  authorize mutation. Unauthorized calls to write tools fail with HTTP 403 before
99
99
  SDK dispatch.
100
100
 
101
+ Every `tools/call`, `resources/list`, `resources/templates/list`, and
102
+ `resources/read` request is also checked against the current socket peer zone
103
+ and the effective policy of every referenced collection before SDK dispatch.
104
+ The check is per request, including batch members and requests that reuse an
105
+ existing session; a session's original peer zone is never reused as authority.
106
+ Non-loopback access therefore requires both a valid bearer token and a
107
+ compatible collection policy. Policy denial returns HTTP 403 with stable
108
+ `EGRESS_DENIED` data and a redacted evaluator audit. Write authorization remains
109
+ an independent additional requirement.
110
+
111
+ The MCP tool and resource registry is paired with a callsite-level
112
+ network-boundary inventory. A TypeScript AST scan covers shipped TS, TSX, JS,
113
+ and JSX and recognizes direct, qualified, bracketed, imported, and aliased
114
+ fetch, EventSource, WebSocket, Bun listener/DNS/socket, HTTP inference, and
115
+ external-process primitives. Every detected invocation has its own stable
116
+ inventory key, so adding a second call in an already-listed file still fails
117
+ the contract test. Browser same-origin calls are classified as client
118
+ transport and name the server route that performs the actual egress check; the
119
+ client scan itself is not treated as content authorization.
120
+
101
121
  Boundary failures use the closed
102
122
  [`mcp-http-error`](./output-schemas/mcp-http-error.schema.json) body with stable,
103
123
  redacted statuses: 401 (authentication), 403 (peer/Host/Origin/write), 413
@@ -697,6 +717,11 @@ Search result items include `contentType` when available and always include
697
717
  human-oriented; structured clients should read `structuredContent.results`.
698
718
  Structured result items also preserve optional `context` guidance in
699
719
  global-to-specific order without changing the result `uri` or `docid`.
720
+ File/export records also include bounded `record` provenance in structured
721
+ search/get/multi-get/Ask output. Their `source.relPath` is the real local export
722
+ container while the unique `uri` remains the required exact retrieval ref. The
723
+ closed `record.adapter` object identifies the adapter ID, version, and
724
+ configuration fingerprint used to produce the logical record.
700
725
  When `explain: true`, `structuredContent.meta.explain` includes deterministic
701
726
  stage lines and per-result score receipts. Non-neutral configured content-type
702
727
  rules add `contentTypeBoost` with base/raw scores, factor, capped contribution,
@@ -2341,6 +2366,52 @@ MCP Server Status
2341
2366
 
2342
2367
  ---
2343
2368
 
2369
+ ## Collection Egress Policy and Audit Tools
2370
+
2371
+ Read tools:
2372
+
2373
+ - `gno_egress_policy_get { collection }` returns configured/effective policy,
2374
+ provenance source, current monotonic revision, and diagnostic version.
2375
+ - `gno_egress_check` accepts exact collection scope, action, destination zone,
2376
+ caller authentication/authorization, content class, and partial mode. It
2377
+ performs no action and returns the canonical decision/explanation contract.
2378
+ An explicit scope is a non-empty, duplicate-free list of at most 64 canonical
2379
+ collection names; omitted scope means all configured collections.
2380
+ - `gno_egress_audit_list`, `gno_egress_audit_show`, and
2381
+ `gno_egress_audit_status` expose only content-free local receipts.
2382
+
2383
+ Write-enabled tools:
2384
+
2385
+ - `gno_egress_policy_set` requires confirmation
2386
+ `{ collection, currentPolicy, currentRevision, targetPolicy, acknowledged: true }`
2387
+ for relaxation. The revision is durable and one-use; replayed, stale,
2388
+ cross-collection, and cross-target confirmations fail closed.
2389
+ - `gno_egress_audit_delete` deletes exactly one receipt.
2390
+ - `gno_egress_audit_purge { confirm: true }` deletes only audit receipts.
2391
+
2392
+ Policy does not replace transport authentication or `enableWrite`. Local audit
2393
+ inspection remains available when outbound content is denied. Retrieval-trace
2394
+ export resolves exact trace lineage and checks policy before creating or
2395
+ reusing an export receipt; missing traces return `NOT_FOUND` before policy
2396
+ evaluation.
2397
+
2398
+ All collection-policy and audit tool inputs are closed objects. Unknown
2399
+ top-level fields are rejected by MCP input validation before any handler,
2400
+ mutation, invalidation, or audit operation runs.
2401
+
2402
+ Active resident requests retain their admission authorization epoch. A policy
2403
+ mutation may advance its own request to the new epoch, but older REST and MCP
2404
+ responses are replaced by a content-free `EGRESS_POLICY_CHANGED` retry
2405
+ response/event before protected bytes are emitted, including streaming
2406
+ responses. `/api/events` binds each SSE subscriber to its admission epoch and
2407
+ rechecks it before every document event and heartbeat; a stale subscriber
2408
+ receives only the retry event and is then unsubscribed.
2409
+
2410
+ Schemas: `collection-egress-policy.schema.json`,
2411
+ `collection-egress-policy-set.schema.json`,
2412
+ `collection-egress-check.schema.json`, and
2413
+ `egress-audit-management.schema.json`.
2414
+
2344
2415
  ## See Also
2345
2416
 
2346
2417
  - [CLI Specification](./cli.md)
@@ -95,6 +95,35 @@
95
95
  }
96
96
  ],
97
97
  "$defs": {
98
+ "egressLineage": {
99
+ "type": "object",
100
+ "additionalProperties": false,
101
+ "required": ["effectivePolicy", "digest", "sources"],
102
+ "properties": {
103
+ "effectivePolicy": { "enum": ["local_only", "lan", "remote"] },
104
+ "digest": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
105
+ "sources": {
106
+ "type": "array",
107
+ "minItems": 1,
108
+ "maxItems": 128,
109
+ "items": {
110
+ "type": "object",
111
+ "additionalProperties": false,
112
+ "required": ["collection", "policy", "source"],
113
+ "properties": {
114
+ "collection": {
115
+ "type": "string",
116
+ "pattern": "^[a-z0-9][a-z0-9_-]{0,63}$"
117
+ },
118
+ "policy": { "enum": ["local_only", "lan", "remote"] },
119
+ "source": {
120
+ "enum": ["explicit", "config_default", "legacy_default"]
121
+ }
122
+ }
123
+ }
124
+ }
125
+ }
126
+ },
98
127
  "searchResult": {
99
128
  "type": "object",
100
129
  "required": ["docid", "score", "uri", "snippet", "source"],
@@ -127,10 +156,136 @@
127
156
  "type": "string",
128
157
  "description": "Resolved user-configured retrieval guidance, ordered from global to collection to matching path prefixes"
129
158
  },
159
+ "egressLineage": { "$ref": "#/$defs/egressLineage" },
130
160
  "snippet": {
131
161
  "type": "string",
132
162
  "description": "Matching text snippet from document mirror"
133
163
  },
164
+ "record": {
165
+ "type": "object",
166
+ "additionalProperties": false,
167
+ "description": "Bounded logical-record provenance for a file/export source",
168
+ "required": ["recordKey", "sourceLocator", "anchors", "adapter"],
169
+ "properties": {
170
+ "recordKey": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
171
+ "sourceLocator": {
172
+ "type": "string",
173
+ "minLength": 1,
174
+ "maxLength": 512
175
+ },
176
+ "anchors": {
177
+ "type": "array",
178
+ "maxItems": 256,
179
+ "items": {
180
+ "type": "object",
181
+ "additionalProperties": false,
182
+ "required": ["kind", "value"],
183
+ "properties": {
184
+ "kind": {
185
+ "enum": [
186
+ "line",
187
+ "cue",
188
+ "timestamp",
189
+ "message",
190
+ "event",
191
+ "record"
192
+ ]
193
+ },
194
+ "value": {
195
+ "type": "string",
196
+ "minLength": 1,
197
+ "maxLength": 512
198
+ },
199
+ "endValue": {
200
+ "type": "string",
201
+ "minLength": 1,
202
+ "maxLength": 512
203
+ }
204
+ }
205
+ }
206
+ },
207
+ "adapter": {
208
+ "type": "object",
209
+ "additionalProperties": false,
210
+ "required": ["id", "version", "fingerprint"],
211
+ "properties": {
212
+ "id": { "type": "string", "minLength": 1, "maxLength": 128 },
213
+ "version": {
214
+ "type": "string",
215
+ "minLength": 1,
216
+ "maxLength": 64
217
+ },
218
+ "fingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
219
+ }
220
+ },
221
+ "author": { "type": "string", "maxLength": 2048 },
222
+ "participants": {
223
+ "type": "array",
224
+ "maxItems": 256,
225
+ "items": { "type": "string", "minLength": 1, "maxLength": 2048 }
226
+ },
227
+ "categories": {
228
+ "type": "array",
229
+ "maxItems": 256,
230
+ "items": { "type": "string", "minLength": 1, "maxLength": 512 }
231
+ },
232
+ "dateFields": {
233
+ "type": "object",
234
+ "maxProperties": 256,
235
+ "propertyNames": { "type": "string", "maxLength": 128 },
236
+ "additionalProperties": {
237
+ "type": "string",
238
+ "minLength": 1,
239
+ "maxLength": 256
240
+ }
241
+ },
242
+ "messageId": { "type": "string", "maxLength": 2048 },
243
+ "inReplyTo": { "type": "string", "maxLength": 2048 },
244
+ "references": {
245
+ "type": "array",
246
+ "maxItems": 256,
247
+ "items": {
248
+ "type": "string",
249
+ "minLength": 1,
250
+ "maxLength": 2048
251
+ }
252
+ },
253
+ "threadId": { "type": "string", "maxLength": 2048 },
254
+ "eventId": { "type": "string", "maxLength": 2048 },
255
+ "sessionId": { "type": "string", "maxLength": 2048 },
256
+ "attachments": {
257
+ "type": "array",
258
+ "maxItems": 256,
259
+ "items": {
260
+ "type": "object",
261
+ "additionalProperties": false,
262
+ "required": ["name"],
263
+ "properties": {
264
+ "name": {
265
+ "type": "string",
266
+ "minLength": 1,
267
+ "maxLength": 512
268
+ },
269
+ "mime": {
270
+ "type": "string",
271
+ "minLength": 1,
272
+ "maxLength": 256
273
+ },
274
+ "bytes": {
275
+ "type": "integer",
276
+ "minimum": 0,
277
+ "maximum": 9007199254740991
278
+ },
279
+ "disposition": { "enum": ["inline", "attachment"] },
280
+ "sha256": {
281
+ "type": "string",
282
+ "pattern": "^[a-f0-9]{64}$"
283
+ }
284
+ }
285
+ }
286
+ }
287
+ }
288
+ },
134
289
  "snippetRange": {
135
290
  "type": "object",
136
291
  "description": "Line range of snippet in mirror content",
@@ -306,7 +461,7 @@
306
461
  "schemaVersion": { "const": "1.0" },
307
462
  "mode": { "const": "closed_capsule" },
308
463
  "capsule": {
309
- "$ref": "gno://schemas/context-capsule-v1@1.0"
464
+ "$ref": "gno://schemas/context-capsule-v1@1.1"
310
465
  },
311
466
  "freshness": {
312
467
  "$ref": "gno://schemas/context-capsule-verification@1.0"
@@ -18,6 +18,7 @@
18
18
  "destination": {
19
19
  "$ref": "gno://schemas/browser-clip@1.0#/properties/destination"
20
20
  },
21
+ "egressLineage": { "$ref": "#/definitions/egressLineage" },
21
22
  "tags": {
22
23
  "type": "array",
23
24
  "items": { "type": "string" }
@@ -48,6 +49,35 @@
48
49
  }
49
50
  },
50
51
  "definitions": {
52
+ "egressLineage": {
53
+ "type": "object",
54
+ "additionalProperties": false,
55
+ "required": ["effectivePolicy", "digest", "sources"],
56
+ "properties": {
57
+ "effectivePolicy": { "enum": ["local_only", "lan", "remote"] },
58
+ "digest": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
59
+ "sources": {
60
+ "type": "array",
61
+ "minItems": 1,
62
+ "uniqueItems": true,
63
+ "items": {
64
+ "type": "object",
65
+ "additionalProperties": false,
66
+ "required": ["collection", "policy", "source"],
67
+ "properties": {
68
+ "collection": {
69
+ "type": "string",
70
+ "pattern": "^[a-z0-9][a-z0-9_-]{0,63}$"
71
+ },
72
+ "policy": { "enum": ["local_only", "lan", "remote"] },
73
+ "source": {
74
+ "enum": ["explicit", "config_default", "legacy_default"]
75
+ }
76
+ }
77
+ }
78
+ }
79
+ }
80
+ },
51
81
  "source": {
52
82
  "type": "object",
53
83
  "additionalProperties": false,
@@ -0,0 +1,91 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://gno.sh/schemas/collection-egress-check.schema.json",
4
+ "title": "Collection egress check and explanation",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": [
8
+ "schemaVersion",
9
+ "mode",
10
+ "allowedCollections",
11
+ "omittedCollections",
12
+ "disclosure",
13
+ "lineage",
14
+ "decision",
15
+ "remediation"
16
+ ],
17
+ "properties": {
18
+ "schemaVersion": { "const": "1.0" },
19
+ "mode": { "enum": ["complete", "partial", "denied"] },
20
+ "allowedCollections": {
21
+ "type": "array",
22
+ "items": { "type": "string" },
23
+ "uniqueItems": true
24
+ },
25
+ "omittedCollections": {
26
+ "type": "array",
27
+ "items": {
28
+ "type": "object",
29
+ "additionalProperties": false,
30
+ "required": ["collection", "reason"],
31
+ "properties": {
32
+ "collection": { "type": "string" },
33
+ "reason": { "type": "string" }
34
+ }
35
+ }
36
+ },
37
+ "disclosure": {
38
+ "oneOf": [
39
+ { "type": "null" },
40
+ {
41
+ "type": "object",
42
+ "additionalProperties": false,
43
+ "required": ["code", "omittedCount", "omittedCollections"],
44
+ "properties": {
45
+ "code": { "const": "EGRESS_PARTIAL_RESULT" },
46
+ "omittedCount": { "type": "integer", "minimum": 1 },
47
+ "omittedCollections": {
48
+ "type": "array",
49
+ "items": { "type": "string" },
50
+ "minItems": 1,
51
+ "uniqueItems": true
52
+ }
53
+ }
54
+ }
55
+ ]
56
+ },
57
+ "lineage": {
58
+ "type": "object",
59
+ "required": ["effectivePolicy", "digest", "sources"],
60
+ "properties": {
61
+ "effectivePolicy": { "enum": ["local_only", "lan", "remote"] },
62
+ "digest": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
63
+ "sources": { "type": "array", "minItems": 1 }
64
+ }
65
+ },
66
+ "decision": {
67
+ "type": "object",
68
+ "required": ["allowed", "code", "reason", "audit"],
69
+ "properties": {
70
+ "allowed": { "type": "boolean" },
71
+ "code": { "enum": ["EGRESS_ALLOWED", "EGRESS_DENIED"] },
72
+ "reason": { "type": "string" },
73
+ "audit": { "type": "object" }
74
+ }
75
+ },
76
+ "remediation": {
77
+ "oneOf": [
78
+ { "type": "null" },
79
+ {
80
+ "type": "object",
81
+ "additionalProperties": false,
82
+ "required": ["code", "message"],
83
+ "properties": {
84
+ "code": { "type": "string" },
85
+ "message": { "type": "string", "minLength": 1 }
86
+ }
87
+ }
88
+ ]
89
+ }
90
+ }
91
+ }