wendkeep 0.87.0 → 0.88.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 (36) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.en.md +1 -1
  3. package/README.md +1 -1
  4. package/docs/en/commands/observer-security.md +154 -0
  5. package/docs/en/commands/observer.md +30 -12
  6. package/docs/pt-BR/commands/observer-security.md +154 -0
  7. package/docs/pt-BR/commands/observer.md +30 -12
  8. package/hooks/observer-publish.mjs +3 -1
  9. package/package.json +2 -1
  10. package/packages/mcp/src/executor.mjs +35 -2
  11. package/packages/observer/package.json +16 -0
  12. package/packages/observer/src/audit.mjs +1 -0
  13. package/packages/observer/src/authz.mjs +38 -0
  14. package/packages/observer/src/encryption.mjs +75 -0
  15. package/packages/observer/src/index.mjs +7 -0
  16. package/packages/observer/src/policy.mjs +305 -0
  17. package/packages/observer/src/purge.mjs +100 -0
  18. package/packages/observer/src/redaction.mjs +54 -0
  19. package/packages/observer/src/retention.mjs +39 -0
  20. package/packages/observer/src/token-registry.mjs +122 -0
  21. package/schema/observer/006-observer-security.sql +64 -0
  22. package/schema/observer-policy-v1.schema.json +63 -0
  23. package/schema/sync-event-v1.schema.json +10 -0
  24. package/src/observer-auth.mjs +8 -0
  25. package/src/observer-privacy.mjs +7 -3
  26. package/src/observer-publish.mjs +31 -0
  27. package/src/observer-server.mjs +179 -20
  28. package/src/observer-sql-migrate.mjs +5 -2
  29. package/src/observer-sql-publish.mjs +114 -39
  30. package/src/observer-sql-store.mjs +299 -45
  31. package/src/observer-transcript-store.mjs +23 -8
  32. package/src/observer.mjs +145 -12
  33. package/src/sync-protocol.mjs +20 -0
  34. package/web/observer/app.mjs +107 -31
  35. package/web/observer/index.html +7 -0
  36. package/web/observer/styles.css +5 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,24 @@ All notable changes to **wendkeep** are documented here. Format based on
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this project follows
5
5
  [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.88.0] — 2026-08-29
8
+
9
+ ### Added
10
+
11
+ - **Políticas oficiais e RBAC do Observer por projeto.** Captura de transcripts, uso, memória e
12
+ sync passa por policy única, redaction por classe/campo e matriz explícita de papéis e scopes;
13
+ tokens são armazenados somente por hash, escopados, expirados, revogáveis e rotacionáveis.
14
+ - **Ciclo de vida auditável.** Retenção e purge idempotentes ficam disponíveis por CLI e endpoint,
15
+ com receipts retriáveis, tratamento de chegadas tardias e reconciliação segura do índice FTS.
16
+
17
+ ### Security
18
+
19
+ - **Criptografia fail-closed no Observer.** Quando exigida, ingestão, outbox e publicação recusam
20
+ plaintext; o backfill protege dados legados antes de leituras, e backups de migração v5 usam
21
+ AES-256-GCM, manifest de integridade, permissões restritas e restore autenticado.
22
+ - **Bootstrap remoto sem credencial irrestrita.** Server, MCP, CLI e Compose usam o registry
23
+ hash-only com projeto, papel, scopes e expiração explícitos, incluindo revogação e rotação.
24
+
7
25
  ## [0.87.0] — 2026-08-29
8
26
 
9
27
  ### Added
package/README.en.md CHANGED
@@ -261,7 +261,7 @@ The README is the map; the guides provide syntax, options, exit codes, examples,
261
261
  | **Costs and observability** | safe dry-run, tri-state, aggregation, trends, and historical rebuild | [Costs and observability](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/costs-and-observability.md) |
262
262
  | **Maintenance and diagnostics** | doctor, frontier/manifest freshness, drift, version, and help | [Maintenance and diagnostics](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/maintenance-and-diagnostics.md) |
263
263
  | **Evidence-based commits** | `wendkeep commit`, tasks derived from contracts, tests only from canonical execution bound to the exact SHA, opt-in Git hooks, privacy, and remote range gate | [Commits](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/commit.md) |
264
- | **Local Observer** | `observer serve`, registration, incremental publication, `reconcile`, outbox, and multi-project index | [Local Observer](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/observer.md) |
264
+ | **Local Observer** | `observer serve`, scoped/expiring hash-only bootstrap, incremental publishing with canonical timestamps, `reconcile`, policy, stable structural identities, content-bound hashes, deletion-safe capture, required encryption, retention, purge, and multi-project index | [Local Observer](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/observer.md) · [Security](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/observer-security.md) |
265
265
 
266
266
  Operations that deserve step-by-step guidance: [verify and exits 0/1/2](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/verify.md),
267
267
  [causal TDD attestation](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/tdd.md),
package/README.md CHANGED
@@ -261,7 +261,7 @@ The README is the map; the guides provide syntax, options, exit codes, examples,
261
261
  | **Costs and observability** | safe dry-run, tri-state, aggregation, trends, and historical rebuild | [Costs and observability](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/costs-and-observability.md) |
262
262
  | **Maintenance and diagnostics** | doctor, frontier/manifest freshness, drift, version, and help | [Maintenance and diagnostics](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/maintenance-and-diagnostics.md) |
263
263
  | **Evidence-based commits** | `wendkeep commit`, tasks derived from contracts, tests only from canonical execution bound to the exact SHA, opt-in Git hooks, privacy, and remote range gate | [Commits](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/commit.md) |
264
- | **Local Observer** | `observer serve`, registration, incremental publication, `reconcile`, outbox, and multi-project index | [Local Observer](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/observer.md) |
264
+ | **Local Observer** | `observer serve`, scoped/expiring hash-only bootstrap, incremental publishing with canonical timestamps, `reconcile`, policy, stable structural identities, content-bound hashes, deletion-safe capture, required encryption, retention, purge, and multi-project index | [Local Observer](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/observer.md) · [Security](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/observer-security.md) |
265
265
 
266
266
  Operations that deserve step-by-step guidance: [verify and exits 0/1/2](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/verify.md),
267
267
  [causal TDD attestation](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/tdd.md),
@@ -0,0 +1,154 @@
1
+ # Observer security
2
+
3
+ **English** · [Português](../../pt-BR/commands/observer-security.md)
4
+
5
+ ## Purpose
6
+
7
+ The Observer is a local/team read model, never a new authority over the Vault, specs, memory, or
8
+ sync. Its threat model assumes a compromised remote host, stolen token, curious operator, copied
9
+ database/outbox, adversarial payload, and interrupted purge. Host/Origin validation remains active;
10
+ mutations and sensitive reads fail closed, including on loopback.
11
+
12
+ | Class | Default | Primary risk |
13
+ |---|---|---|
14
+ | document | `metadata` | full memory/decisions |
15
+ | transcript | `metadata` | conversation and tools |
16
+ | prompt/response | `redacted` | PII and secrets |
17
+ | usage | `aggregate` | cost and operational identity |
18
+ | audit/receipt | minimal metadata | deleting the proof itself |
19
+
20
+ Policy v1 restricts by class, `project_id`, path glob, and `entity_type`. Later rules win only in
21
+ the matching project. Redaction covers Bearer values, URL/connection-string credentials, access
22
+ keys, email, phone, and safe configurable regular expressions. Its schema is
23
+ `schema/observer-policy-v1.schema.json`.
24
+ With `transcript_capture: messages`, arrays, JSONL, and the canonical `{messages:[...]}` envelope
25
+ retain only `user|assistant|system` messages with string `role`/`content` after redaction; extra
26
+ fields, tool records, and malformed entries are dropped or fail closed.
27
+ Explicit policy is the publisher's sole capture authority; `WENDKEEP_OBSERVER_CAPTURE_LEVEL` is
28
+ only translated into policy for legacy callers that supplied no policy file, and can never elevate
29
+ or suppress explicit `none|metadata|messages|full` or `selected` documents.
30
+ For document/transcript upserts, `content_hash` always binds the final content after capture and
31
+ redaction; metadata/selected capture uses the SHA-256 of empty content. Document deletions remain
32
+ effective even when content capture is `none`, preserve their path/revision/operation metadata,
33
+ and never carry stale content or content hashes.
34
+ Redaction never rewrites validated structural identity fields such as project/event/entity IDs,
35
+ logical paths, revisions, or operations. Path privacy is enforced fail-closed by project/path
36
+ capture rules; it is not implemented by renaming a storage key through a content-redaction rule.
37
+ The per-event structural contract also preserves accepted snake/camel aliases, document/session/
38
+ agent/call/transcript/rollup keys, timestamps, roles, status, coverage, model/pricing dimensions,
39
+ workflow, and source provenance. `title`, `summary`, `agent_name`, content, prompt/response, and
40
+ metadata remain redactable display/content fields.
41
+ During incremental publishing, missing or blank turn timestamps inherit the batch's canonical
42
+ instant, numeric epoch milliseconds are normalized to ISO 8601, and invalid non-empty values fail
43
+ closed before policy/store; the event and payload use the same instant.
44
+
45
+ ## When to use
46
+
47
+ Use it before enabling Observer for real data, registering or revoking credentials, restricting
48
+ capture, protecting SQLite/outbox, defining retention, or deleting data with verifiable proof.
49
+
50
+ ## When not to use
51
+
52
+ Do not use it as a corporate KMS/secret manager, to publish Vault/runtime data, to replace local
53
+ authority, or to delete tables and indexes manually. `full` capture remains opt-in and subject to
54
+ policy/redaction.
55
+
56
+ ## Prerequisites
57
+
58
+ Use Node.js 22.13+, keep the bind on loopback, and inject tokens/keys only through environment
59
+ variables. The bootstrap token is registered hash-only with explicit projects and a finite expiry;
60
+ it is not a registry-bypassing wildcard admin. For Docker, also set
61
+ `WENDKEEP_OBSERVER_BOOTSTRAP_PROJECTS`, `WENDKEEP_OBSERVER_BOOTSTRAP_EXPIRES_AT`, and a 32-byte
62
+ hex/base64 `WENDKEEP_OBSERVER_ENCRYPTION_KEY`. The operator owns the key and external receipts.
63
+
64
+ ## Syntax
65
+
66
+ ```bash
67
+ npx wendkeep observer serve --token <token> --bootstrap-projects <p1,p2> --bootstrap-expires-at <ISO> [--bootstrap-token-id <id>] [--require-loopback-auth] [--require-encryption]
68
+ npx wendkeep observer security token create --project-id <project> --role <role> --scopes <scopes> --token-env <env> --expires-at <ISO>
69
+ npx wendkeep observer security token rotate --project-id <project> --token-id <id> --token-env <env> --expires-at <ISO> [--new-token-id <id>]
70
+ npx wendkeep observer security token revoke --project-id <project> --token-id <id>
71
+ npx wendkeep observer security policy set --project-id <project> --file <policy.json>
72
+ npx wendkeep observer security policy show --project-id <project>
73
+ npx wendkeep observer security purge --project-id <project> --before <ISO> --classes <classes> [--dry-run]
74
+ npx wendkeep observer security retention run --project-id <project> [--dry-run] [--operation-id <id>]
75
+ ```
76
+
77
+ ## Options and exit codes
78
+
79
+ - `viewer` reads metadata/aggregates; `auditor` may receive sensitive scopes; `publisher` ingests;
80
+ `admin` manages policy, purge, and recovery. Role, scope, and project must authorize together.
81
+ - Tokens persist only as SHA-256; expiry, rotation, and revocation take effect without restart.
82
+ - After rotating bootstrap credentials, update both token and token ID in the environment; restart
83
+ never reactivates an expired or revoked predecessor.
84
+ - `--token-env` names the variable holding the secret; the command never prints its value.
85
+ - `--require-loopback-auth` protects the whole API; sensitive reads require a token without it too.
86
+ - `--require-encryption` fails when external key material is missing or invalid.
87
+ - `WENDKEEP_OBSERVER_REQUIRE_ENCRYPTION=1` applies the same fail-closed behavior to `status`,
88
+ `security`, `register`, `publish`, and `reconcile`; with a configured key, every first v5 upgrade
89
+ creates only `.bak.enc` plus its manifest before any read/backfill.
90
+ - Exit `0` means the operation completed; exit `1` means invalid configuration, authorization,
91
+ policy, key, or operation. The hook retains fail-open exit `0` for the local workflow, but aborts
92
+ before persisting unsafe content.
93
+
94
+ Audit stores capability, outcome, route, and time, never a Bearer value, prompt, response, or payload.
95
+
96
+ ## Examples
97
+
98
+ Explicit audited offline recovery:
99
+
100
+ ```powershell
101
+ $env:OBSERVER_RECOVERY_TOKEN = '<strong-temporary-secret>'
102
+ npx wendkeep observer security token create --data-dir C:\WendKeepObserver `
103
+ --project-id project-a --role admin --scopes '*' --token-env OBSERVER_RECOVERY_TOKEN `
104
+ --expires-at 2026-09-29T12:00:00Z --reason 'offline recovery' --json
105
+ npx wendkeep observer security token revoke --data-dir C:\WendKeepObserver `
106
+ --project-id project-a --token-id <id> --reason 'recovery complete' --json
107
+ ```
108
+
109
+ Always dry-run purge first. The retention runner is explicit and idempotent (CLI or
110
+ `POST /v1/projects/:id/security/retention`), with no hidden timer:
111
+
112
+ ```powershell
113
+ npx wendkeep observer security purge --data-dir C:\WendKeepObserver `
114
+ --project-id project-a --before 2026-08-01T00:00:00Z `
115
+ --classes documents,calls,transcripts --dry-run --json
116
+ ```
117
+
118
+ ```powershell
119
+ npx wendkeep observer security retention run --data-dir C:\WendKeepObserver `
120
+ --project-id project-a --operation-id scheduled-2026-08-29 --dry-run --json
121
+ ```
122
+
123
+ ## Expected result
124
+
125
+ TTL is independent for documents, calls, and transcripts. Counts, projection/FTS removal, events,
126
+ and receipt share one transaction; retry is idempotent and late old data creates new proof.
127
+
128
+ AES-256-GCM uses project/class/record/field AAD and an external `keyProvider`. The v6 backfill
129
+ removes plaintext and derived indexes before reads; a wrong key fails without disclosing content.
130
+ Structural migration `006-observer-security.sql` creates a backup, checks checksum, rolls back, and
131
+ supports retry. In required at-rest mode the backup is `.bak.enc`, carries a manifest/key ID and
132
+ restrictive permissions, fails restore with a wrong key, and leaves no plaintext `.bak` behind.
133
+
134
+ The hook applies metadata/redacted policy by default; `WENDKEEP_OBSERVER_POLICY_FILE` selects an
135
+ explicit policy. `WENDKEEP_OBSERVER_OUTBOX_KEY_ENV` names the outbox key variable and
136
+ `WENDKEEP_OBSERVER_OUTBOX_KEY_ID` identifies the key. Compose requires authentication and
137
+ encryption. The dashboard keeps Bearer only in memory, exports a sanitized copy, and exposes
138
+ Security. MCP requires scopes for calls/full search. Sync carries only `policy_ref`, without
139
+ duplicating tokens or authority.
140
+
141
+ ## Common errors and diagnosis
142
+
143
+ - `observer_token_missing|expired|revoked`: create/rotate a scoped token or use offline recovery.
144
+ - `observer_project_forbidden|role_forbidden|scope_forbidden`: check project/role/scope intersection.
145
+ - `observer_encryption_key_unavailable|observer_decryption_failed`: check key ID/material; never
146
+ weaken required mode.
147
+ - `observer_policy_invalid`: validate fields/captures and remove invalid or explosive regexes.
148
+ - v6 migration failure: preserve `.pre-006-*.bak.enc` and its manifest, correct the cause, and retry.
149
+
150
+ ## Next steps
151
+
152
+ Read [Local Observer](observer.md), dry-run retention, validate revoked/expired tokens, and keep the
153
+ receipt outside the database when external proof is required. Never publish the database, backup,
154
+ outbox, key, token, or `/data`.
@@ -26,7 +26,9 @@ WendKeep hooks.
26
26
 
27
27
  Use Node.js 22.13 or newer for the SQL Observer. Keep Core and the remaining commands continue to
28
28
  support Node.js 18 or newer. Explicitly register each project and set `WENDKEEP_OBSERVER_TOKEN`;
29
- loopback reads remain open, while every mutation requires a Bearer token.
29
+ every mutation and every sensitive-content read requires a Bearer token, including on loopback.
30
+ Metadata and aggregates may remain locally open when `--require-loopback-auth` is omitted. See
31
+ [Observer security](observer-security.md).
30
32
 
31
33
  ## Syntax
32
34
 
@@ -36,7 +38,7 @@ npx wendkeep observer register --project <project> --vault <vault> --data-dir <d
36
38
  npx wendkeep observer publish --project <project> --vault <vault> --data-dir <directory>
37
39
  npx wendkeep observer reconcile --project <project> --vault <vault> --data-dir <directory> [--url http://127.0.0.1:8787]
38
40
  npx wendkeep observer memory import --project <project> --vault <vault> --url http://127.0.0.1:8787 --token <token> --json
39
- npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir <directory> --token <token>
41
+ npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir <directory> --token <token> --bootstrap-projects <p1,p2> --bootstrap-expires-at <ISO> [--require-loopback-auth] [--require-encryption]
40
42
  ```
41
43
 
42
44
  ## Options and exit codes
@@ -46,7 +48,13 @@ npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir <directory>
46
48
  - `--project` and `--vault` identify a project for `register`, `publish`, `reconcile`, and `memory import`.
47
49
  - `--host` accepts only `127.0.0.1`, `localhost`, or `::1`; other hosts are rejected before
48
50
  listening.
49
- - `--token` or `WENDKEEP_OBSERVER_TOKEN` authenticates mutations; `--allow-non-loopback` fails without one.
51
+ - `--token` or `WENDKEEP_OBSERVER_TOKEN` supplies hash-only bootstrap material; explicit projects
52
+ and finite expiry are required, and all mutations/sensitive reads go through the registry;
53
+ `--allow-non-loopback` fails without one.
54
+ - `--require-loopback-auth` also requires Bearer for local metadata and aggregates and enables the
55
+ project's secure ingestion policy.
56
+ - `--require-encryption` requires a 32-byte hex/base64 `WENDKEEP_OBSERVER_ENCRYPTION_KEY`; use
57
+ `WENDKEEP_OBSERVER_ENCRYPTION_KEY_ID` to identify the external key.
50
58
  - `WENDKEEP_OBSERVER_CAPTURE_LEVEL` accepts `metadata` (default, no messages), `messages`, or
51
59
  `full-transcript`. Absolute local paths are never published.
52
60
  - Exit `0` means success; exit `1` means configuration or operation failure; the publisher hook
@@ -57,7 +65,10 @@ npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir <directory>
57
65
  ```powershell
58
66
  npx wendkeep observer register --project C:\GitHub\WendKeep --vault C:\GitHub\WendKeep\.WendKeep-vault --data-dir C:\WendKeepObserver
59
67
  $env:WENDKEEP_OBSERVER_TOKEN = '<strong-local-token>'
60
- npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir C:\WendKeepObserver --token $env:WENDKEEP_OBSERVER_TOKEN
68
+ $env:WENDKEEP_OBSERVER_BOOTSTRAP_PROJECTS = 'project-a'
69
+ $env:WENDKEEP_OBSERVER_BOOTSTRAP_EXPIRES_AT = '2026-09-29T12:00:00Z'
70
+ $env:WENDKEEP_OBSERVER_ENCRYPTION_KEY = '<32-bytes-in-hex-or-base64>'
71
+ npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir C:\WendKeepObserver --token $env:WENDKEEP_OBSERVER_TOKEN --require-loopback-auth
61
72
  $env:WENDKEEP_OBSERVER_URL = 'http://127.0.0.1:8787'
62
73
  ```
63
74
 
@@ -67,15 +78,19 @@ For local Docker:
67
78
  docker compose -f docker/wendkeep-observer/compose.yaml up -d --build
68
79
  ```
69
80
 
81
+ Compose requires token, bootstrap allowlist/expiry, and key material; it starts with full
82
+ authentication and required encryption. An `encryption_required` policy refuses plaintext ingest/outbox.
83
+
70
84
  ## Local web dashboard
71
85
 
72
86
  With the server running, open [http://127.0.0.1:8787/](http://127.0.0.1:8787/) in a browser. The
73
- dashboard is served by the same process and opens directly for reads, without a form or token. Keep the port
74
- bound to the computer loopback; do not expose this address on a network interface.
87
+ dashboard is served by the same process. Enter the token in the local form: it remains only in page
88
+ memory, is sent as Bearer for queries, and is discarded on reload. Keep the port bound to the
89
+ computer loopback; do not expose this address on a network interface.
75
90
 
76
91
  The dashboard shows the multi-project list, version, health, latest session, active change, change
77
92
  count, and last capture time. Opening a project exposes Overview, Consumption, Sessions, Memory,
78
- Changes, and Sync screens. Consumption shows total cost, token categories, primary agents,
93
+ Changes, Sync, and Security screens. Consumption shows total cost, token categories, primary agents,
79
94
  subagents, providers, models, daily trend, historical coverage, and calls with prompt, response,
80
95
  and transcript content according to the selected capture level. Loading, empty, unavailable-server, conflict, no-pricing, and stale-data
81
96
  states are visible, with manual refresh and an automatic 15-second refresh.
@@ -160,18 +175,21 @@ complete, archive, repair, or promote state.
160
175
  - `POST /v1/projects/:project_id/ingest` — idempotent batches of documents, sessions, agents,
161
176
  rollups, calls, and transcripts.
162
177
  - `GET /v1/projects/:project_id/memory/tree` — document tree and metadata.
163
- - `GET /v1/projects/:project_id/memory/document?path=...` — complete Markdown content.
178
+ - `GET /v1/projects/:project_id/memory/document?path=...` — complete Markdown content; requires Bearer.
164
179
  - `GET /v1/projects/:project_id/memory/search?q=...` — ranked chunk search with matching passage
165
- and provenance; uses a lexical fallback when FTS5 is unavailable.
180
+ and provenance; uses a lexical fallback when FTS5 is unavailable and requires Bearer.
166
181
  - `GET /v1/projects/:project_id/sync` — mode, counts, conflicts, and latest event.
167
182
  - `PUT /v1/projects/:project_id/sync` — compatibility configuration; SQL remains authoritative.
168
- - `GET /v1/projects/:project_id/memory/export` — read-only export with complete content.
183
+ - `GET /v1/projects/:project_id/memory/export` — read-only export sanitized by default; requires Bearer.
169
184
  - `POST /v1/projects/:project_id/memory/events` — idempotent batch ingestion.
170
185
  - `GET /v1/projects/:project_id/usage/summary` — filterable totals by period, change, session,
171
186
  agent, provider, model, and role.
172
187
  - `GET /v1/projects/:project_id/usage/breakdown` — agent, subagent, and model hierarchy.
173
- - `GET /v1/projects/:project_id/usage/calls` — individual calls with prompt and response.
174
- - `GET /v1/projects/:project_id/transcripts/:transcript_id` — compressed transcript validated by hash.
188
+ - `GET /v1/projects/:project_id/usage/calls` — individual calls with prompt and response; requires Bearer.
189
+ - `GET /v1/projects/:project_id/transcripts/:transcript_id` — compressed transcript validated by hash; requires Bearer.
190
+ - `GET /v1/projects/:project_id/security` — policy, token counts, and sanitized audit; requires admin.
191
+ - `PUT /v1/projects/:project_id/security/policy` — updates the effective policy without restart; requires admin.
192
+ - `POST /v1/projects/:project_id/security/purge` — transactional dry-run/purge with receipt; requires admin.
175
193
 
176
194
  The `/v1` routes reject transported or expanded bodies above their limits and validate project,
177
195
  path, revision, hash, idempotency, and isolation before writing to SQLite. Use `memory/export` for
@@ -0,0 +1,154 @@
1
+ # Segurança do Observer
2
+
3
+ **PT-BR** · [English](../../en/commands/observer-security.md)
4
+
5
+ ## Objetivo
6
+
7
+ O Observer é um read model local ou de equipe, nunca uma nova autoridade sobre Vault, spec, memória
8
+ ou sync. O modelo de ameaça considera host remoto comprometido, token roubado, operador curioso,
9
+ banco/outbox copiados, payload adversarial e purge interrompido. Host/Origin continuam validados;
10
+ mutações e leituras sensíveis falham fechadas, inclusive no loopback.
11
+
12
+ | Classe | Padrão | Risco principal |
13
+ |---|---|---|
14
+ | documento | `metadata` | memória/decisões integrais |
15
+ | transcript | `metadata` | conversa e ferramentas |
16
+ | prompt/resposta | `redacted` | PII e segredos |
17
+ | uso | `aggregate` | custo e identidade operacional |
18
+ | audit/receipt | metadados mínimos | apagar a própria prova |
19
+
20
+ A policy v1 restringe por classe, `project_id`, glob de path e `entity_type`. Regras mais tardias
21
+ vencem apenas no projeto correspondente. Redaction cobre Bearer, credenciais em URL/connection
22
+ string, access keys, e-mail, telefone e regexes configuráveis seguras. O schema é
23
+ `schema/observer-policy-v1.schema.json`.
24
+ Em `transcript_capture: messages`, arrays, JSONL e o envelope canônico `{messages:[...]}` preservam
25
+ somente mensagens `user|assistant|system` com `role`/`content` string após redaction; campos extras,
26
+ tools e entradas malformadas são descartados ou falham fechados.
27
+ A policy explícita é a única autoridade de captura do publisher; `WENDKEEP_OBSERVER_CAPTURE_LEVEL`
28
+ é apenas compatibilidade traduzida para policy quando nenhum arquivo de policy foi fornecido e
29
+ nunca eleva nem suprime `none|metadata|messages|full` ou documentos `selected` explícitos.
30
+ Nos upserts de documento/transcript, `content_hash` sempre representa o conteúdo final após captura
31
+ e redaction; captura metadata/selected usa o SHA-256 do conteúdo vazio. Exclusões de documento
32
+ continuam efetivas mesmo com captura `none`, preservam path/revision/operação e nunca transportam
33
+ conteúdo ou hash obsoleto.
34
+ A redaction nunca reescreve campos validados de identidade estrutural, como IDs de projeto/evento/
35
+ entidade, paths lógicos, revisions ou operações. A privacidade do path é aplicada de modo fail-closed
36
+ pelas regras de captura por projeto/path, não pela renomeação da chave de storage por uma regra de
37
+ redaction de conteúdo.
38
+ O contrato estrutural por evento também preserva aliases snake/camel aceitos, chaves de documento/
39
+ sessão/agente/call/transcript/rollup, timestamps, roles, status, coverage, dimensões de modelo/preço,
40
+ workflow e proveniência de source. `title`, `summary`, `agent_name`, conteúdo, prompt/resposta e
41
+ metadata continuam como campos de display/conteúdo sujeitos a redaction.
42
+ Na publicação incremental, timestamps de turn ausentes ou vazios herdam o instante canônico do
43
+ lote, epoch numérico em milissegundos é normalizado para ISO 8601 e valor não vazio inválido falha
44
+ fechado antes da policy/store; evento e payload usam o mesmo instante.
45
+
46
+ ## Quando usar
47
+
48
+ Use ao habilitar o Observer para dados reais, cadastrar ou revogar credenciais, restringir captura,
49
+ proteger SQLite/outbox, definir retenção ou eliminar dados com prova verificável.
50
+
51
+ ## Quando não usar
52
+
53
+ Não use como KMS/secret manager corporativo, para publicar Vault/runtime, para substituir a
54
+ autoridade local ou para apagar manualmente tabelas e índices. Captura `full` continua opt-in e
55
+ sujeita à policy/redaction.
56
+
57
+ ## Pré-requisitos
58
+
59
+ Use Node.js 22.13+, mantenha o bind no loopback e injete tokens/chaves somente por variáveis de
60
+ ambiente. O token de bootstrap é registrado somente pelo hash, exige projetos explícitos e
61
+ expiração finita; não é um admin wildcard fora do registry. Para Docker, defina também
62
+ `WENDKEEP_OBSERVER_BOOTSTRAP_PROJECTS`, `WENDKEEP_OBSERVER_BOOTSTRAP_EXPIRES_AT` e uma chave de
63
+ 32 bytes em hex/base64 em `WENDKEEP_OBSERVER_ENCRYPTION_KEY`. O operador guarda a chave e receipts externos.
64
+
65
+ ## Sintaxe
66
+
67
+ ```bash
68
+ npx wendkeep observer serve --token <token> --bootstrap-projects <p1,p2> --bootstrap-expires-at <ISO> [--bootstrap-token-id <id>] [--require-loopback-auth] [--require-encryption]
69
+ npx wendkeep observer security token create --project-id <projeto> --role <role> --scopes <scopes> --token-env <env> --expires-at <ISO>
70
+ npx wendkeep observer security token rotate --project-id <projeto> --token-id <id> --token-env <env> --expires-at <ISO> [--new-token-id <id>]
71
+ npx wendkeep observer security token revoke --project-id <projeto> --token-id <id>
72
+ npx wendkeep observer security policy set --project-id <projeto> --file <policy.json>
73
+ npx wendkeep observer security policy show --project-id <projeto>
74
+ npx wendkeep observer security purge --project-id <projeto> --before <ISO> --classes <classes> [--dry-run]
75
+ npx wendkeep observer security retention run --project-id <projeto> [--dry-run] [--operation-id <id>]
76
+ ```
77
+
78
+ ## Opções e códigos de saída
79
+
80
+ - `viewer` lê metadata/agregados; `auditor` pode receber scopes sensíveis; `publisher` ingere;
81
+ `admin` administra policy, purge e recovery. Role, scope e projeto precisam autorizar juntos.
82
+ - Tokens são persistidos somente como SHA-256; expiração, rotação e revogação valem sem restart.
83
+ - Após rotacionar o bootstrap, atualize token e token ID no ambiente; reiniciar nunca reativa a
84
+ credencial antiga revogada ou expirada.
85
+ - `--token-env` nomeia a variável com o segredo; o comando nunca imprime o valor.
86
+ - `--require-loopback-auth` protege toda a API; reads sensíveis exigem token mesmo sem a flag.
87
+ - `--require-encryption` falha se a chave externa estiver ausente ou inválida.
88
+ - `WENDKEEP_OBSERVER_REQUIRE_ENCRYPTION=1` aplica a mesma falha fechada a `status`, `security`,
89
+ `register`, `publish` e `reconcile`; com chave configurada, todo primeiro upgrade v5 usa apenas
90
+ `.bak.enc` + manifest antes de qualquer leitura/backfill.
91
+ - Exit `0` indica operação concluída; exit `1` indica configuração, autorização, policy, chave ou
92
+ operação inválida. O hook mantém exit `0` fail-open para o fluxo local, mas aborta antes de
93
+ persistir conteúdo inseguro.
94
+
95
+ O audit guarda capability, resultado, rota e horário, nunca Bearer, prompt, resposta ou payload.
96
+
97
+ ## Exemplos
98
+
99
+ Recovery offline explícito e auditado:
100
+
101
+ ```powershell
102
+ $env:OBSERVER_RECOVERY_TOKEN = '<segredo-forte-temporário>'
103
+ npx wendkeep observer security token create --data-dir C:\WendKeepObserver `
104
+ --project-id project-a --role admin --scopes '*' --token-env OBSERVER_RECOVERY_TOKEN `
105
+ --expires-at 2026-09-29T12:00:00Z --reason 'offline recovery' --json
106
+ npx wendkeep observer security token revoke --data-dir C:\WendKeepObserver `
107
+ --project-id project-a --token-id <id> --reason 'recovery complete' --json
108
+ ```
109
+
110
+ Sempre faça dry-run antes do purge. O runner de retenção é explícito/idempotente (CLI ou
111
+ `POST /v1/projects/:id/security/retention`), sem timer oculto:
112
+
113
+ ```powershell
114
+ npx wendkeep observer security purge --data-dir C:\WendKeepObserver `
115
+ --project-id project-a --before 2026-08-01T00:00:00Z `
116
+ --classes documents,calls,transcripts --dry-run --json
117
+ ```
118
+
119
+ ```powershell
120
+ npx wendkeep observer security retention run --data-dir C:\WendKeepObserver `
121
+ --project-id project-a --operation-id scheduled-2026-08-29 --dry-run --json
122
+ ```
123
+
124
+ ## Resultado esperado
125
+
126
+ TTL é independente para documentos, calls e transcripts. Contagens, remoção de projeções/FTS,
127
+ eventos e receipt usam a mesma transação; retry é idempotente e dado antigo tardio gera nova prova.
128
+
129
+ AES-256-GCM usa AAD por projeto/classe/registro/campo e `keyProvider` externo. O backfill v6 remove
130
+ plaintext e índices derivados antes de liberar leituras; chave errada falha sem revelar conteúdo.
131
+ A migration estrutural `006-observer-security.sql` cria backup, valida checksum, faz rollback e
132
+ permite retry. Em modo at-rest obrigatório, o backup é `.bak.enc`, tem manifest/key ID/permissão
133
+ restrita e restauração falha com chave errada; nenhum `.bak` plaintext permanece.
134
+
135
+ O hook aplica policy metadata/redacted por padrão; `WENDKEEP_OBSERVER_POLICY_FILE` seleciona policy
136
+ explícita. `WENDKEEP_OBSERVER_OUTBOX_KEY_ENV` nomeia a variável da chave da outbox e
137
+ `WENDKEEP_OBSERVER_OUTBOX_KEY_ID` identifica a chave. O Compose exige autenticação e criptografia.
138
+ O painel guarda Bearer somente em memória, exporta cópia sanitizada e expõe Segurança. MCP exige
139
+ scope para calls/busca integral. Sync leva apenas `policy_ref`, sem duplicar tokens ou autoridade.
140
+
141
+ ## Erros comuns e diagnóstico
142
+
143
+ - `observer_token_missing|expired|revoked`: crie/rotacione um token escopado ou faça recovery offline.
144
+ - `observer_project_forbidden|role_forbidden|scope_forbidden`: confira a interseção projeto/role/scope.
145
+ - `observer_encryption_key_unavailable|observer_decryption_failed`: confira key ID e material externo;
146
+ nunca enfraqueça o modo obrigatório.
147
+ - `observer_policy_invalid`: valide campos/captures e remova regex inválida ou explosiva.
148
+ - Falha de migration v6: preserve `.pre-006-*.bak.enc` e seu manifest, corrija a causa e repita.
149
+
150
+ ## Próximos passos
151
+
152
+ Leia [Observer local](observer.md), faça um dry-run de retenção, valide token revogado/expirado e
153
+ guarde o receipt fora do banco quando precisar de prova externa. Nunca publique banco, backup,
154
+ outbox, chave, token ou `/data`.
@@ -26,7 +26,9 @@ pelos hooks e pelo WendKeep local.
26
26
 
27
27
  Tenha Node.js 22.13 ou mais recente para executar o Observer SQL. O Keep Core e os demais comandos
28
28
  continuam compatíveis com Node.js 18 ou mais recente. Registre explicitamente cada projeto e defina
29
- `WENDKEEP_OBSERVER_TOKEN`; leituras no loopback permanecem abertas, mas toda mutação exige Bearer.
29
+ `WENDKEEP_OBSERVER_TOKEN`. Toda mutação e toda leitura de conteúdo sensível exigem Bearer, inclusive
30
+ no loopback; metadados e agregados podem permanecer abertos localmente quando
31
+ `--require-loopback-auth` não é usado. Veja [Segurança do Observer](observer-security.md).
30
32
 
31
33
  ## Sintaxe
32
34
 
@@ -36,7 +38,7 @@ npx wendkeep observer register --project <projeto> --vault <vault> --data-dir <d
36
38
  npx wendkeep observer publish --project <projeto> --vault <vault> --data-dir <diretório>
37
39
  npx wendkeep observer reconcile --project <projeto> --vault <vault> --data-dir <diretório> [--url http://127.0.0.1:8787]
38
40
  npx wendkeep observer memory import --project <projeto> --vault <vault> --url http://127.0.0.1:8787 --token <token> --json
39
- npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir <diretório> --token <token>
41
+ npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir <diretório> --token <token> --bootstrap-projects <p1,p2> --bootstrap-expires-at <ISO> [--require-loopback-auth] [--require-encryption]
40
42
  ```
41
43
 
42
44
  ## Opções e códigos de saída
@@ -46,7 +48,13 @@ npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir <diretório>
46
48
  - `--project` e `--vault` identificam o projeto nos comandos `register`, `publish`, `reconcile` e `memory import`.
47
49
  - `--host` aceita somente `127.0.0.1`, `localhost` ou `::1`; outros hosts são recusados antes do
48
50
  listen.
49
- - `--token` ou `WENDKEEP_OBSERVER_TOKEN` autentica mutações; `--allow-non-loopback` falha sem token.
51
+ - `--token` ou `WENDKEEP_OBSERVER_TOKEN` fornece o segredo de bootstrap hash-only; projetos
52
+ explícitos e expiração finita são obrigatórios, e toda mutação/leitura sensível passa pelo registry;
53
+ `--allow-non-loopback` falha sem token.
54
+ - `--require-loopback-auth` exige Bearer também para metadados e agregados locais e ativa a policy
55
+ segura do projeto na ingestão.
56
+ - `--require-encryption` exige `WENDKEEP_OBSERVER_ENCRYPTION_KEY` com 32 bytes em hex/base64; use
57
+ `WENDKEEP_OBSERVER_ENCRYPTION_KEY_ID` para identificar a chave externa.
50
58
  - `WENDKEEP_OBSERVER_CAPTURE_LEVEL` aceita `metadata` (padrão, sem mensagens), `messages` ou
51
59
  `full-transcript`. Caminhos locais absolutos nunca são publicados.
52
60
  - Exit `0` indica sucesso; exit `1` indica falha de configuração ou operação; o hook publisher
@@ -57,7 +65,10 @@ npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir <diretório>
57
65
  ```powershell
58
66
  npx wendkeep observer register --project C:\GitHub\WendKeep --vault C:\GitHub\WendKeep\.WendKeep-vault --data-dir C:\WendKeepObserver
59
67
  $env:WENDKEEP_OBSERVER_TOKEN = '<token-local-forte>'
60
- npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir C:\WendKeepObserver --token $env:WENDKEEP_OBSERVER_TOKEN
68
+ $env:WENDKEEP_OBSERVER_BOOTSTRAP_PROJECTS = 'project-a'
69
+ $env:WENDKEEP_OBSERVER_BOOTSTRAP_EXPIRES_AT = '2026-09-29T12:00:00Z'
70
+ $env:WENDKEEP_OBSERVER_ENCRYPTION_KEY = '<32-bytes-em-hex-ou-base64>'
71
+ npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir C:\WendKeepObserver --token $env:WENDKEEP_OBSERVER_TOKEN --require-loopback-auth
61
72
  $env:WENDKEEP_OBSERVER_URL = 'http://127.0.0.1:8787'
62
73
  ```
63
74
 
@@ -67,15 +78,19 @@ Para Docker local:
67
78
  docker compose -f docker/wendkeep-observer/compose.yaml up -d --build
68
79
  ```
69
80
 
81
+ O Compose exige token, allowlist/expiração do bootstrap e chave; inicia com autenticação integral
82
+ e criptografia obrigatória. A policy `encryption_required` recusa ingestão e outbox plaintext.
83
+
70
84
  ## Painel web local
71
85
 
72
86
  Com o servidor em execução, abra [http://127.0.0.1:8787/](http://127.0.0.1:8787/) no navegador.
73
- O painel é servido pelo mesmo processo e abre diretamente para consultas, sem formulário ou token. A porta fica
74
- presa ao loopback do computador; não coloque o endereço em uma interface de rede.
87
+ O painel é servido pelo mesmo processo. Informe o token no formulário local: ele fica somente na
88
+ memória da página, segue como Bearer nas consultas e é descartado ao recarregar. A porta fica presa
89
+ ao loopback do computador; não coloque o endereço em uma interface de rede.
75
90
 
76
91
  O painel mostra a lista multi-projeto, versão, saúde, sessão mais recente, change ativa, contagem
77
92
  de changes e data da última captura. Ao abrir um projeto, o workspace oferece Overview, Consumo,
78
- Sessões, Memória, Changes e Sincronização. A aba Consumo mostra custo total, tokens por categoria,
93
+ Sessões, Memória, Changes, Sincronização e Segurança. A aba Consumo mostra custo total, tokens por categoria,
79
94
  agentes principais, subagentes, provedores, modelos, tendência diária, cobertura histórica e
80
95
  chamadas conforme o nível de captura escolhido. Os estados de carregamento, vazio, servidor
81
96
  indisponível, conflito, modelo sem tarifa e dados desatualizados ficam visíveis, e a atualização
@@ -160,18 +175,21 @@ corte. As telas do Observer não concluem, arquivam, reparam ou promovem estado.
160
175
  - `POST /v1/projects/:project_id/ingest` — lote idempotente de documentos, sessões, agentes, rollups,
161
176
  chamadas e transcripts.
162
177
  - `GET /v1/projects/:project_id/memory/tree` — árvore e metadados dos documentos.
163
- - `GET /v1/projects/:project_id/memory/document?path=...` — conteúdo Markdown integral.
178
+ - `GET /v1/projects/:project_id/memory/document?path=...` — conteúdo Markdown integral; exige Bearer.
164
179
  - `GET /v1/projects/:project_id/memory/search?q=...` — busca ranqueada por chunks, com trecho do
165
- match e proveniência; usa fallback lexical quando FTS5 não está disponível.
180
+ match e proveniência; usa fallback lexical quando FTS5 não está disponível e exige Bearer.
166
181
  - `GET /v1/projects/:project_id/sync` — modo, contagem, conflitos e último evento.
167
182
  - `PUT /v1/projects/:project_id/sync` — compatibilidade de configuração; a autoridade continua SQL.
168
- - `GET /v1/projects/:project_id/memory/export` — exportação read-only com conteúdo completo.
183
+ - `GET /v1/projects/:project_id/memory/export` — exportação read-only sanitizada por padrão; exige Bearer.
169
184
  - `POST /v1/projects/:project_id/memory/events` — ingestão idempotente em lote.
170
185
  - `GET /v1/projects/:project_id/usage/summary` — totais filtráveis por período, change, sessão,
171
186
  agente, provedor, modelo e papel.
172
187
  - `GET /v1/projects/:project_id/usage/breakdown` — hierarquia de agentes, subagentes e modelos.
173
- - `GET /v1/projects/:project_id/usage/calls` — chamadas individuais com prompt e resposta.
174
- - `GET /v1/projects/:project_id/transcripts/:transcript_id` — transcript comprimido, validado por hash.
188
+ - `GET /v1/projects/:project_id/usage/calls` — chamadas individuais com prompt e resposta; exige Bearer.
189
+ - `GET /v1/projects/:project_id/transcripts/:transcript_id` — transcript comprimido, validado por hash; exige Bearer.
190
+ - `GET /v1/projects/:project_id/security` — policy, contagens de tokens e audit sanitizado; exige admin.
191
+ - `PUT /v1/projects/:project_id/security/policy` — atualiza a policy efetiva sem restart; exige admin.
192
+ - `POST /v1/projects/:project_id/security/purge` — dry-run/purge transacional com receipt; exige admin.
175
193
 
176
194
  As rotas `/v1` rejeitam corpo transportado ou expandido acima do limite e validam projeto, caminho,
177
195
  revisão, hash, idempotência e isolamento antes de gravar o conteúdo no SQLite. Para preservar uma
@@ -1,15 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import { pathToFileURL } from 'node:url';
3
3
  import { debugLog, readHookInput, resolveVault } from './obsidian-common.mjs';
4
- import { publishObserverSnapshot } from '../src/observer-publish.mjs';
4
+ import { publishObserverSnapshot, resolveObserverPublisherSecurity } from '../src/observer-publish.mjs';
5
5
 
6
6
  async function main() {
7
7
  const input = readHookInput();
8
8
  const resolved = resolveVault(input);
9
+ const publisherSecurity = resolveObserverPublisherSecurity();
9
10
  const result = await publishObserverSnapshot({
10
11
  vaultBase: resolved.base,
11
12
  projectRoot: resolved.projectRoot,
12
13
  input,
14
+ ...publisherSecurity,
13
15
  });
14
16
  if (!result.ok && result.error) debugLog('Observer publish fail-open:', result.error);
15
17
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wendkeep",
3
- "version": "0.87.0",
3
+ "version": "0.88.0",
4
4
  "description": "Vault-first persistent memory for AI coding agents, with an optional profile-aware governance runtime: OFF, FLOW, GUIDE, GOVERN, or ASSURE. Local-first and agent-agnostic (Claude Code, Codex, Cursor…).",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -46,6 +46,7 @@
46
46
  "scripts": {
47
47
  "precheck": "node --check src/capabilities.mjs && node --check src/host-capabilities.mjs && node --check src/task-contracts.mjs && node --check src/task-leases.mjs && node --check src/task.mjs && node --check src/change.mjs && node --check src/archive-operation-lock.mjs && node --check src/worktree.mjs && node --check src/worktree-cleanup.mjs && node --check src/provenance-gate.mjs && node --check src/provenance-sources.mjs && node --check src/receipt-ledger.mjs && node --check src/evidence-envelope.mjs && node --check src/context.mjs && node --check src/active-context-health.mjs && node --check src/active-context-runtime.mjs && node --check hooks/active-context-store.mjs && node --check hooks/change-core.mjs && node --check hooks/brain-inject.mjs && node --check hooks/change-context.mjs && node --check hooks/session-stop.mjs && node --check packages/vault/src/worktree-metadata.mjs && node --check packages/vault/src/evidence-envelope.mjs && node --check packages/vault/src/memory-handoff.mjs && node --check packages/integrations/src/capabilities.mjs && node --check packages/pi/src/index.mjs && node --check src/sync-protocol.mjs && node --check src/sync-outbox.mjs && node --check src/sync-adapters.mjs && node --check src/sync-protocol-cli.mjs && node --check packages/mcp/src/sync.mjs && node --check src/portable.mjs && node --check src/tdd.mjs && node --check src/tdd-attestation.mjs && node --check src/tdd-attestation-store.mjs",
48
48
  "check": "node --check scripts/validate-commit-range.mjs && node --check packages/commit/src/index.mjs && node --check packages/commit/src/cli.mjs && node --check packages/commit/src/git-runtime.mjs && node --check src/git-commit-hooks.mjs && node --check scripts/release.mjs && node --check scripts/release-plan.mjs && node --check scripts/release-provenance.mjs && node --check scripts/run-scope.mjs && node --check src/release-provenance.mjs && node --check bin/wendkeep.mjs && node --check packages/cli/src/index.mjs && node --check src/mcp.mjs && node --check src/init.mjs && node --check src/doctor.mjs && node --check src/active-context-health.mjs && node --check src/project-vault.mjs && node --check src/observer-auth.mjs && node --check src/observer-privacy.mjs && node --check src/observer-snapshot.mjs && node --check src/observer-store.mjs && node --check src/observer-memory.mjs && node --check src/observer-memory-publish.mjs && node --check src/observer-sql-store.mjs && node --check src/observer-sql-migrate.mjs && node --check src/observer-sql-publish.mjs && node --check src/observer-transcript-store.mjs && node --check src/observer-server.mjs && node --check src/observer.mjs && node --check src/observer-publish.mjs && node --check src/operating-profile.mjs && node --check src/profile.mjs && node --check src/flow.mjs && node --check src/work-kind.mjs && node --check src/delivery.mjs && node --check web/observer/app.mjs && node --check hooks/observer-publish.mjs && node --check hooks/evidence-context.mjs && node --check hooks/active-context-handoff-evidence.mjs && node --check hooks/evidence-recall.mjs && node --check hooks/memory-scope.mjs && node --check hooks/operating-profile-runtime.mjs && node --check hooks/operating-profile-task-store.mjs && node --check hooks/flow-core.mjs && node --check hooks/flow-protected-policy.mjs && node --check hooks/git-snapshot.mjs && node --check hooks/vault-path-safety.mjs && node --check hooks/vault-runtime-store.mjs && node --check packages/harness/src/index.mjs && node --check packages/harness/src/flow-store.mjs && node --check packages/harness/src/operating-profile.mjs && node --check packages/harness/src/sensors-core.mjs && node --check packages/integrations/src/host-hooks.mjs && node --check packages/integrations/src/hook-envelope.mjs && node --check packages/integrations/src/prompt-content.mjs && node --check packages/integrations/src/transcript-usage.mjs && node --check packages/integrations/src/transcripts.mjs && node --check packages/integrations/src/session-identity.mjs && node --check packages/integrations/src/index.mjs && node --check packages/mcp/src/audit.mjs && node --check packages/mcp/src/cli.mjs && node --check packages/mcp/src/config.mjs && node --check packages/mcp/src/effects.mjs && node --check packages/mcp/src/executor.mjs && node --check packages/mcp/src/server.mjs && node --check packages/mcp/src/stdio.mjs && node --check packages/mcp/src/index.mjs && node --check packages/vault/src/index.mjs && node --check packages/vault/src/project-vault.mjs && node --check packages/vault/src/vault-path-safety.mjs && node --check packages/vault/src/locale.mjs && node --check packages/vault/src/memory-schema.mjs && node --check packages/vault/src/memory-mode.mjs && node --check packages/vault/src/memory-scope.mjs && node --check packages/vault/src/memory-candidate-policy.mjs && node --check packages/vault/src/evidence-recall.mjs && node --check packages/vault/src/memory-handoff.mjs && node --check packages/vault/src/memory-store.mjs && node --check packages/vault/src/validate-core.mjs && node --check packages/vault/src/validate-memory.mjs",
49
+ "postcheck": "node --check packages/observer/src/index.mjs && node --check packages/observer/src/policy.mjs && node --check packages/observer/src/redaction.mjs && node --check packages/observer/src/authz.mjs && node --check packages/observer/src/token-registry.mjs && node --check packages/observer/src/encryption.mjs && node --check packages/observer/src/retention.mjs && node --check packages/observer/src/purge.mjs && node --check packages/observer/src/audit.mjs",
49
50
  "test": "node --test --test-concurrency=2",
50
51
  "test:core": "node scripts/run-scope.mjs core",
51
52
  "release": "node scripts/release.mjs",