Skip to content

Commit 70eeee1

Browse files
authored
release: v2.7.0 with memory defenses (#152)
Release ZettelForge 2.7.0 with write-time MemSAD memory defenses, governance configuration, documentation, telemetry compatibility, and regression coverage.
1 parent b6396f8 commit 70eeee1

16 files changed

Lines changed: 1170 additions & 44 deletions

CHANGELOG.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,31 @@ Versioning follows [Semantic Versioning](https://semver.org/).
66

77
## [Unreleased]
88

9+
## [2.7.0] - 2026-05-26
10+
11+
Security and OSINT release. Adds the RFC-016 OSINT layer and the first
12+
SEC-011 / MemSAD-inspired write-time memory-poisoning defense. No data
13+
migration is required. The new memory defense ships in audit mode by default.
14+
915
### Added
1016

17+
- **OSINT layer scaffold and Phase 1 collectors** (RFC-016). Adds
18+
`zettelforge.osint` with infrastructure, breach, people, social, and
19+
technology collector packages; OSINT ontology and relation extensions;
20+
entity canonicalization helpers; collector registry; and tests covering
21+
DNS, WHOIS, certificates, BGP, ports, breach, people, social, and tech
22+
collector contracts. Optional runtime dependencies are available with
23+
`pip install zettelforge[osint]`.
24+
- **Write-time memory anomaly defense** (SEC-011 / MemSAD). New
25+
`MemoryAnomalyGate` scores candidate notes before persistence using
26+
MemSAD-style max/mean embedding similarity against recent domain
27+
calibration notes plus character n-gram Jensen-Shannon divergence to
28+
reduce synonym/paraphrase evasion. Config lives under
29+
`governance.memory_defense`; default mode is `audit`, with `block` and
30+
`quarantine` available once a deployment has a clean calibration corpus.
31+
- **Quarantine path for rejected memory writes.** In `quarantine` mode,
32+
flagged notes are written to JSONL forensic quarantine and are not exposed
33+
to recall, entity lookup, LanceDB, or graph traversal.
1134
- **CrewAI integration** (issue #40). New optional extra
1235
`pip install zettelforge[crewai]` exposes `ZettelForgeRecallTool`,
1336
`ZettelForgeRememberTool`, and `ZettelForgeSynthesizeTool` as CrewAI
@@ -19,6 +42,23 @@ Versioning follows [Semantic Versioning](https://semver.org/).
1942
Tests gated on `pytest.importorskip("crewai")`; 11 pass against
2043
crewai 1.14.x.
2144

45+
### Changed
46+
47+
- **Telemetry attribution compatibility.** `caller` is now supported in
48+
telemetry events while the previous `actor` field and method arguments
49+
remain backward-compatible for existing dashboards and tests.
50+
- **Real LLM integration and performance tests are explicit opt-in.**
51+
Set `ZETTELFORGE_RUN_LLM_INTEGRATION=1` or
52+
`ZETTELFORGE_RUN_PERFORMANCE_TESTS=1` to run environment-sensitive suites.
53+
The default regression suite is now deterministic on machines without
54+
local LLM credentials or dedicated benchmark hardware.
55+
56+
### Tests
57+
58+
- Added focused memory-defense tests for insufficient calibration, audit-mode
59+
anomaly detection, and quarantine writes.
60+
- Default regression gate: `742 passed, 13 skipped`.
61+
2262
## [2.6.2] - 2026-04-27
2363

2464
UI/UX release. Fixes the `/config` page so the Apply button actually works

config.default.yaml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,10 @@ synthesis:
376376
# ZETTELFORGE_PII_ACTION=redact
377377
# ZETTELFORGE_LIMITS_MAX_CONTENT_LENGTH=104857600
378378
# ZETTELFORGE_LIMITS_RECALL_TIMEOUT=60
379+
# ZETTELFORGE_MEMORY_DEFENSE_ENABLED=true
380+
# ZETTELFORGE_MEMORY_DEFENSE_MODE=audit # audit | block | quarantine
381+
# ZETTELFORGE_MEMORY_DEFENSE_MIN_CALIBRATION=50
382+
# ZETTELFORGE_MEMORY_DEFENSE_KAPPA=2.0
379383
#
380384
governance:
381385
enabled: true
@@ -390,6 +394,17 @@ governance:
390394
limits:
391395
max_content_length: 52428800 # 50 MB, 0 = unlimited
392396
recall_timeout_seconds: 30.0 # seconds, 0 = unlimited
397+
memory_defense:
398+
enabled: true
399+
mode: audit # audit | block | quarantine
400+
min_calibration_notes: 50
401+
max_reference_notes: 50
402+
kappa: 2.0
403+
lexical_weight: 0.25
404+
ngram_size: 3
405+
monitored_domains: [] # empty = all domains
406+
quarantine_path: "" # default: <storage.data_dir>/quarantine/memory_anomalies.jsonl
407+
quarantine_raw_content: true
393408

394409

395410
# ── LanceDB Maintenance (RFC-009 Phase 1.5) ─────────────────────────────────
@@ -487,4 +502,3 @@ web:
487502
enabled: true
488503
host: 0.0.0.0
489504
port: 8088
490-

docs/reference/configuration.md

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ tags:
88
- environment-variables
99
- settings
1010
- deployment
11-
last_updated: "2026-04-25"
12-
version: "2.6.0"
11+
last_updated: "2026-05-26"
12+
version: "2.7.0"
1313
---
1414

1515
# Configuration Reference
@@ -264,6 +264,8 @@ class GovernanceConfig:
264264
enabled: bool = True
265265
min_content_length: int = 1
266266
pii: PIIConfig = field(default_factory=PIIConfig)
267+
limits: LimitsConfig = field(default_factory=LimitsConfig)
268+
memory_defense: MemoryDefenseConfig = field(default_factory=MemoryDefenseConfig)
267269
```
268270

269271
| Key | Type | Default | Env Override | Description |
@@ -309,6 +311,36 @@ class LimitsConfig:
309311
| `governance.limits.max_content_length` | `int` | `52428800` | `ZETTELFORGE_LIMITS_MAX_CONTENT_LENGTH` | Maximum content length in bytes for `remember()`. 0 = unlimited. 50 MB default. |
310312
| `governance.limits.recall_timeout_seconds` | `float` | `30.0` | `ZETTELFORGE_LIMITS_RECALL_TIMEOUT` | Maximum seconds for a recall() query. 0 = unlimited. |
311313

314+
#### governance.memory_defense (SEC-011 / MemSAD)
315+
316+
```python
317+
@dataclass
318+
class MemoryDefenseConfig:
319+
enabled: bool = True
320+
mode: str = "audit"
321+
min_calibration_notes: int = 50
322+
max_reference_notes: int = 50
323+
kappa: float = 2.0
324+
lexical_weight: float = 0.25
325+
ngram_size: int = 3
326+
monitored_domains: list[str] = field(default_factory=list)
327+
quarantine_path: str = ""
328+
quarantine_raw_content: bool = True
329+
```
330+
331+
| Key | Type | Default | Env Override | Description |
332+
|:----|:-----|:--------|:-------------|:------------|
333+
| `governance.memory_defense.enabled` | `bool` | `True` | `ZETTELFORGE_MEMORY_DEFENSE_ENABLED` | Enable write-time memory-poisoning anomaly evaluation before notes are persisted or indexed. |
334+
| `governance.memory_defense.mode` | `str` | `audit` | `ZETTELFORGE_MEMORY_DEFENSE_MODE` | Policy for flagged writes: `audit` logs only, `block` rejects the write, `quarantine` writes a forensic JSONL record and rejects the write. |
335+
| `governance.memory_defense.min_calibration_notes` | `int` | `50` | `ZETTELFORGE_MEMORY_DEFENSE_MIN_CALIBRATION` | Minimum same-domain reference notes required before thresholding. Below this count, writes are allowed with `calibration_insufficient` audit metadata. |
336+
| `governance.memory_defense.max_reference_notes` | `int` | `50` | -- | Maximum recent same-domain reference notes used for calibration and scoring. |
337+
| `governance.memory_defense.kappa` | `float` | `2.0` | `ZETTELFORGE_MEMORY_DEFENSE_KAPPA` | Threshold multiplier: `mean + kappa * stddev` over calibration scores. |
338+
| `governance.memory_defense.lexical_weight` | `float` | `0.25` | -- | Weight applied to character n-gram Jensen-Shannon divergence, complementing embedding similarity against synonym/paraphrase evasion. |
339+
| `governance.memory_defense.ngram_size` | `int` | `3` | -- | Character n-gram size for lexical divergence. |
340+
| `governance.memory_defense.monitored_domains` | `list[str]` | `[]` | -- | Domains to evaluate. Empty list means every domain. |
341+
| `governance.memory_defense.quarantine_path` | `str` | `""` | -- | JSONL quarantine path. Empty uses `<storage.data_dir>/quarantine/memory_anomalies.jsonl`. |
342+
| `governance.memory_defense.quarantine_raw_content` | `bool` | `True` | -- | Include raw rejected content in quarantine records. Disable if quarantine storage is not approved for raw content. |
343+
312344
---
313345

314346
### web (RFC-015)

0 commit comments

Comments
 (0)