zettelforge 2.3.0__tar.gz → 2.4.1__tar.gz
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.
- {zettelforge-2.3.0 → zettelforge-2.4.1}/CHANGELOG.md +140 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/CODE_OF_CONDUCT.md +1 -1
- zettelforge-2.4.1/PKG-INFO +324 -0
- zettelforge-2.4.1/README.md +269 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/SECURITY.md +24 -3
- {zettelforge-2.3.0 → zettelforge-2.4.1}/config.default.yaml +5 -2
- zettelforge-2.4.1/docs/.well-known/security.txt +10 -0
- zettelforge-2.4.1/docs/assets/cf-analytics.js +9 -0
- zettelforge-2.4.1/docs/assets/favicon-16.png +0 -0
- zettelforge-2.4.1/docs/assets/favicon-32.png +0 -0
- zettelforge-2.4.1/docs/assets/favicon-512.png +0 -0
- zettelforge-2.4.1/docs/assets/favicon-64.png +0 -0
- zettelforge-2.4.1/docs/assets/favicon-apple-touch.png +0 -0
- zettelforge-2.4.1/docs/assets/favicon.svg +33 -0
- zettelforge-2.4.1/docs/assets/logo.svg +46 -0
- zettelforge-2.4.1/docs/assets/social-preview.png +0 -0
- zettelforge-2.4.1/docs/assets/threatrecall-lockup-monogram.svg +41 -0
- zettelforge-2.4.1/docs/assets/threatrecall-lockup.svg +39 -0
- zettelforge-2.4.1/docs/assets/threatrecall-logo-flat.svg +29 -0
- zettelforge-2.4.1/docs/assets/zettelforge_architecture-light.svg +105 -0
- zettelforge-2.4.1/docs/assets/zettelforge_architecture.svg +105 -0
- zettelforge-2.4.1/docs/brand/brandIdentity.md +266 -0
- zettelforge-2.4.1/docs/brand/colors_and_type.css +286 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/how-to/configure-typedb.md +6 -4
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/how-to/migrate-jsonl-to-sqlite.md +3 -1
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/how-to/troubleshoot.md +24 -2
- zettelforge-2.4.1/docs/human-evaluation-rubric.md +136 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/index.md +10 -4
- zettelforge-2.4.1/docs/reference/architecture-deep-dive.md +380 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/reference/configuration.md +4 -4
- zettelforge-2.4.1/docs/reference/module-inventory.md +461 -0
- zettelforge-2.4.1/docs/rfcs/RFC-007-operational-telemetry.md +321 -0
- zettelforge-2.4.1/docs/stylesheets/brand-tokens.css +158 -0
- zettelforge-2.4.1/docs/stylesheets/extra.css +70 -0
- zettelforge-2.4.1/docs/stylesheets/fonts/Neuropol.otf +0 -0
- zettelforge-2.4.1/docs/superpowers/research/2026-04-17-test-suite-audit.md +178 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/mkdocs.yml +15 -1
- {zettelforge-2.3.0 → zettelforge-2.4.1}/pyproject.toml +5 -1
- zettelforge-2.4.1/server.json +21 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/__init__.py +1 -1
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/config.py +12 -2
- zettelforge-2.4.1/src/zettelforge/detection/__init__.py +24 -0
- zettelforge-2.4.1/src/zettelforge/detection/base.py +63 -0
- zettelforge-2.4.1/src/zettelforge/detection/consumers.py +77 -0
- zettelforge-2.4.1/src/zettelforge/detection/explainer.py +320 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/integrations/langchain_retriever.py +3 -6
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/memory_manager.py +70 -1
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/ontology.py +129 -4
- zettelforge-2.4.1/src/zettelforge/scripts/human_eval_sampler.py +175 -0
- zettelforge-2.4.1/src/zettelforge/scripts/telemetry_aggregator.py +176 -0
- zettelforge-2.4.1/src/zettelforge/scripts/telemetry_dashboard.py +256 -0
- zettelforge-2.4.1/src/zettelforge/sigma/__init__.py +31 -0
- zettelforge-2.4.1/src/zettelforge/sigma/cli.py +123 -0
- zettelforge-2.4.1/src/zettelforge/sigma/entities.py +249 -0
- zettelforge-2.4.1/src/zettelforge/sigma/ingest.py +234 -0
- zettelforge-2.4.1/src/zettelforge/sigma/parser.py +148 -0
- zettelforge-2.4.1/src/zettelforge/sigma/schemas/NOTICE.md +10 -0
- zettelforge-2.4.1/src/zettelforge/sigma/schemas/__init__.py +1 -0
- zettelforge-2.4.1/src/zettelforge/sigma/schemas/sigma-correlation-rules-schema.json +326 -0
- zettelforge-2.4.1/src/zettelforge/sigma/schemas/sigma-detection-rule-schema.json +247 -0
- zettelforge-2.4.1/src/zettelforge/sigma/schemas/sigma-filters-schema.json +101 -0
- zettelforge-2.4.1/src/zettelforge/sigma/tags.py +86 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/sqlite_backend.py +252 -153
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/storage_backend.py +9 -0
- zettelforge-2.4.1/src/zettelforge/telemetry.py +340 -0
- zettelforge-2.4.1/src/zettelforge/yara/__init__.py +27 -0
- zettelforge-2.4.1/src/zettelforge/yara/cccs_metadata.py +258 -0
- zettelforge-2.4.1/src/zettelforge/yara/cli.py +177 -0
- zettelforge-2.4.1/src/zettelforge/yara/entities.py +269 -0
- zettelforge-2.4.1/src/zettelforge/yara/ingest.py +327 -0
- zettelforge-2.4.1/src/zettelforge/yara/parser.py +110 -0
- zettelforge-2.4.1/src/zettelforge/yara/schemas/CCCS_YARA.yml +306 -0
- zettelforge-2.4.1/src/zettelforge/yara/schemas/CCCS_YARA_values.yml +160 -0
- zettelforge-2.4.1/src/zettelforge/yara/schemas/NOTICE.md +11 -0
- zettelforge-2.4.1/src/zettelforge/yara/schemas/__init__.py +1 -0
- zettelforge-2.4.1/src/zettelforge/yara/tags.py +74 -0
- zettelforge-2.4.1/tests/fixtures/sigma/cloud_example.yml +20 -0
- zettelforge-2.4.1/tests/fixtures/sigma/correlation_example.yml +14 -0
- zettelforge-2.4.1/tests/fixtures/sigma/process_creation_example.yml +17 -0
- zettelforge-2.4.1/tests/fixtures/sigma/tagged_example.yml +30 -0
- zettelforge-2.4.1/tests/fixtures/yara/malware_hash.yar +19 -0
- zettelforge-2.4.1/tests/fixtures/yara/technique_loader.yar +23 -0
- zettelforge-2.4.1/tests/fixtures/yara/webshell.yar +19 -0
- zettelforge-2.4.1/tests/test_causal_extraction.py +102 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_config.py +53 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_conversational_entities.py +50 -11
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_core.py +2 -2
- zettelforge-2.4.1/tests/test_detection_explainer.py +328 -0
- zettelforge-2.4.1/tests/test_detection_rule_entities.py +104 -0
- zettelforge-2.4.1/tests/test_human_eval_sampler.py +125 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_langchain_retriever.py +5 -1
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_llm_client.py +63 -1
- zettelforge-2.4.1/tests/test_sigma_entities.py +205 -0
- zettelforge-2.4.1/tests/test_sigma_ingest.py +212 -0
- zettelforge-2.4.1/tests/test_sigma_parser.py +125 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_sqlite_backend.py +76 -2
- zettelforge-2.4.1/tests/test_telemetry_aggregator.py +142 -0
- zettelforge-2.4.1/tests/test_telemetry_collector.py +351 -0
- zettelforge-2.4.1/tests/test_telemetry_dashboard.py +238 -0
- zettelforge-2.4.1/tests/test_telemetry_integration.py +263 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_two_phase_e2e.py +18 -15
- zettelforge-2.4.1/tests/test_yara_entities.py +170 -0
- zettelforge-2.4.1/tests/test_yara_ingest.py +126 -0
- zettelforge-2.4.1/tests/test_yara_parser.py +126 -0
- zettelforge-2.3.0/PKG-INFO +0 -285
- zettelforge-2.3.0/README.md +0 -233
- zettelforge-2.3.0/docs/assets/logo.svg +0 -3
- zettelforge-2.3.0/docs/assets/social-preview.png +0 -0
- zettelforge-2.3.0/docs/assets/threatrecall-logo.svg +0 -22
- zettelforge-2.3.0/docs/assets/threatrecall-mark.svg +0 -12
- zettelforge-2.3.0/docs/assets/zettelforge_architecture.svg +0 -77
- zettelforge-2.3.0/docs/stylesheets/extra.css +0 -10
- zettelforge-2.3.0/tests/test_causal_extraction.py +0 -91
- {zettelforge-2.3.0 → zettelforge-2.4.1}/.github/CODEOWNERS +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/.github/SECURITY.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/.github/dependabot.yml +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/.github/pull_request_template.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/.github/workflows/ci.yml +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/.github/workflows/docs.yml +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/.github/workflows/publish.yml +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/.github/workflows/snyk-security.yml +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/.gitignore +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/ARCHITECTURE.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/CODEOWNERS +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/CONTRIBUTING.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/Dockerfile +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/GOVERNANCE.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/LICENSE +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/MANIFEST.in +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/BENCHMARK_REPORT.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/LOCOMO_BENCHMARK_COMPARISON.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/auto_ralph.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/benchmark_harness.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/cti_benchmark_v2.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/cti_retrieval_benchmark.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/cti_retrieval_results.json +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/cti_v2_results.json +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/ctibench_benchmark.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/ctibench_results.json +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/dataset.json +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/enterprise-attack.json +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/evolve_benchmark.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/evolve_results.json +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/graph_test.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/locomo_benchmark.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/locomo_results.json +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/locomo_results_v1.3.0_baseline.json +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/memoryagentbench.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/memoryagentbench_results.json +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/mempalace_benchmark.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/mempalace_results.json +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/naive_memory.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/opencti_benchmark.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/ragas_benchmark.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/ragas_cti_results.json +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/ragas_results.json +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/results/benchmark_report.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/results/ralph_optimization_log.json +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/scale_benchmark.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/benchmarks/scale_results.json +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/config.example.yaml +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docker/docker-compose.yml +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/CNAME +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/architecture-diagram.mmd +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/archive/PACKAGE_SUMMARY.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/archive/README.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/archive/SKILL.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/assets/ZettelForge_Architecture.mmd +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/assets/architecture-overview.mmd +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/assets/architecture-read-path.mmd +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/assets/architecture-write-path.mmd +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/assets/demo.gif +0 -0
- /zettelforge-2.3.0/docs/assets/favicon.svg → /zettelforge-2.4.1/docs/assets/favicon-old.svg +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/assets/threatrecall-logo-philosophy.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/assets/threatrecall-logo.png +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/assets/threatrecall-mark.png +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/explanation/architecture.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/explanation/epistemic-tiers.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/explanation/stix-in-zettelforge.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/explanation/two-phase-pipeline.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/explanation/zettelkasten-philosophy.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/how-to/configure-lancedb.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/how-to/configure-opencti.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/how-to/ingest-news-report.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/how-to/integrate-llm-agent.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/how-to/query-apt-tools.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/how-to/reproduce-benchmarks.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/how-to/resolve-aliases.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/how-to/run-temporal-query.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/how-to/store-threat-actor.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/how-to/upgrade.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/llms.txt +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/narrative/2026-04-16-the-memory-problem.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/overrides/main.html +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/reference/governance-controls.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/reference/memory-manager-api.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/reference/retrieval-policies.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/reference/stix-schema.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/rfcs/RFC-001-conversational-entity-extractor.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/rfcs/RFC-002-universal-llm-provider.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/rfcs/RFC-003-adversarial-review.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/rfcs/RFC-003-read-path-depth-routing.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/superpowers/research/2026-04-09-ctibench-ragas-benchmarks.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/superpowers/research/2026-04-09-fastembed-local-embeddings.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/superpowers/research/2026-04-09-hybrid-typedb-lancedb-architecture.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/superpowers/research/2026-04-09-local-llm-llama-cpp.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/superpowers/research/2026-04-15-anti-aversion-cleanup.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/superpowers/research/2026-04-15-causal-graph.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/superpowers/research/2026-04-15-ctibench-ate-fix.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/superpowers/research/2026-04-15-format-stability.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/superpowers/research/2026-04-15-memory-evolution.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/superpowers/research/2026-04-15-merge-consolidation.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/superpowers/research/2026-04-15-persistence-semantics.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/superpowers/research/2026-04-15-sqlite-migration.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/superpowers/research/README.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/superpowers/specs/2026-04-15-p1-features-prd.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/tutorials/01-quickstart.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/docs/tutorials/02-first-cti-report.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/examples/athf_bridge.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/examples/mcp_claude_code.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/examples/quickstart.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/governance/controls.yaml +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/scripts/migrate_jsonl_to_sqlite.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/scripts/rebuild_index.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/scripts/record-demo.sh +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/scripts/typedb-setup.sh +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/scripts/zettelforge-rebuild.service +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/scripts/zettelforge-rebuild.timer +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/skills/claude-code-skill.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/skills/openclaw-skill.md +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/__main__.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/alias_resolver.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/backend_factory.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/blended_retriever.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/cache.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/consolidation.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/demo.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/edition.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/entity_indexer.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/extensions.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/fact_extractor.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/governance_validator.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/graph_retriever.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/integrations/__init__.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/intent_classifier.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/json_parse.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/knowledge_graph.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/llm_client.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/llm_providers/__init__.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/llm_providers/base.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/llm_providers/local_provider.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/llm_providers/mock_provider.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/llm_providers/ollama_provider.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/llm_providers/registry.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/log.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/mcp/__init__.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/mcp/__main__.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/mcp/server.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/memory_evolver.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/memory_store.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/memory_updater.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/note_constructor.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/note_schema.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/observability.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/ocsf.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/retry.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/synthesis_generator.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/synthesis_validator.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/vector_memory.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/src/zettelforge/vector_retriever.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/__init__.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/benchmark_scale.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/conftest.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_basic.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_blended_retriever.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_consolidation.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_cti_integration.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_edition.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_embedding.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_extensions.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_fact_extractor.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_governance.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_governance_spec_drift.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_graph_retriever.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_intent_classifier.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_json_parse.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_llm_providers.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_logging_compliance.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_mcp_server.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_memory_evolver.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_memory_updater.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_performance.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_recall_integration.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_sqlite_integration.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_storage_backend.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_temporal_graph.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/tests/test_typedb_client.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/web/app.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/web/auth.py +0 -0
- {zettelforge-2.3.0 → zettelforge-2.4.1}/web/mcp_server.py +0 -0
|
@@ -6,6 +6,146 @@ Versioning follows [Semantic Versioning](https://semver.org/).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [2.4.1] - 2026-04-24
|
|
10
|
+
|
|
11
|
+
Operational telemetry (RFC-007), TypeDB authentication hardening, and a
|
|
12
|
+
tranche of SQLite backend correctness fixes surfaced by the sqlite
|
|
13
|
+
review in issue #83.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **Operational telemetry** (RFC-007, #85) — per-query recall /
|
|
18
|
+
synthesis metrics captured to `~/.amem/telemetry/telemetry_YYYY-MM-DD.jsonl`
|
|
19
|
+
when `ZETTELFORGE_LOG_LEVEL=DEBUG`. Five shipped components:
|
|
20
|
+
- `TelemetryCollector` class (`start_query` / `log_recall` /
|
|
21
|
+
`log_synthesis` / `log_feedback` / `auto_feedback_from_synthesis`)
|
|
22
|
+
with INFO/DEBUG-gated field sets, 1-hour TTL on in-memory query
|
|
23
|
+
context, and thread-safe JSONL append.
|
|
24
|
+
- `MemoryManager` integration — `recall()` and `synthesize()` gain a
|
|
25
|
+
non-breaking `actor=` kwarg; OCSF events extended via the
|
|
26
|
+
sanctioned `unmapped` object with a `zf_` prefix (class_uid 6002
|
|
27
|
+
compliant). `recall()` wraps `retriever.retrieve()` and
|
|
28
|
+
`graph_retriever.retrieve_note_ids()` with narrow-scope
|
|
29
|
+
`perf_counter` deltas for `vector_latency_ms` / `graph_latency_ms`.
|
|
30
|
+
- Daily aggregator (`python -m zettelforge.scripts.telemetry_aggregator`)
|
|
31
|
+
emitting a `DailyMetrics` JSON report (latency averages, tier
|
|
32
|
+
distribution, unused-notes count, top-utility notes).
|
|
33
|
+
- Human-evaluation workflow — 6-question rubric (`docs/human-evaluation-rubric.md`),
|
|
34
|
+
sampler script (`python -m zettelforge.scripts.human_eval_sampler`)
|
|
35
|
+
that selects 20 random briefings as a fill-in Markdown template,
|
|
36
|
+
and a `--write-events` path to append `event_type: "human_eval"`
|
|
37
|
+
entries back to telemetry.
|
|
38
|
+
- Optional Streamlit dashboard (`streamlit run
|
|
39
|
+
src/zettelforge/scripts/telemetry_dashboard.py`) — query volume,
|
|
40
|
+
latency p50/p95/max, tier distribution, utility trend,
|
|
41
|
+
unused-notes warning.
|
|
42
|
+
- Privacy contract: raw note content never persisted (IDs / tiers /
|
|
43
|
+
source_types / domains only); query text truncated at 200 chars
|
|
44
|
+
INFO / 500 chars DEBUG; local-only, no network calls.
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
|
|
48
|
+
- **SQLite shutdown NPE** (#84, issue #83 H3) — `close()` and
|
|
49
|
+
`initialize()` are now lock-protected and idempotent. Readers and
|
|
50
|
+
writers raise a clean `BackendClosedError` (new, in
|
|
51
|
+
`storage_backend`) instead of the opaque `AttributeError: 'NoneType'
|
|
52
|
+
object has no attribute 'execute'` seen 170× in production logs on
|
|
53
|
+
2026-04-23 during atexit. `memory_manager._enrichment_loop` and
|
|
54
|
+
`_drain_enrichment_queue` catch `BackendClosedError` and exit
|
|
55
|
+
cleanly.
|
|
56
|
+
- **SQLite torn snapshot** (#84, issue #83 C1) — `export_snapshot()`
|
|
57
|
+
now uses `sqlite3.Connection.backup()` for a page-consistent copy.
|
|
58
|
+
The previous `shutil.copy2` path could produce a corrupt backup
|
|
59
|
+
missing `-wal` / `-shm` sidecars, unsafe for DR restore.
|
|
60
|
+
- **SQLite reindex race** (#84, issue #83 C2) — `reindex_vector()` now
|
|
61
|
+
uses a single-lock targeted `UPDATE` on the `embedding_vector`
|
|
62
|
+
column. The previous `get_note_by_id → rewrite_note` path spanned
|
|
63
|
+
two lock acquisitions and could clobber concurrent
|
|
64
|
+
`mark_access_dirty` / `evolve` / supersede edits via
|
|
65
|
+
`INSERT OR REPLACE`.
|
|
66
|
+
|
|
67
|
+
### Security
|
|
68
|
+
|
|
69
|
+
- **TypeDB authentication hardening** (#82) — removed known-insecure
|
|
70
|
+
`admin` / `password` defaults from `TypeDBConfig` and
|
|
71
|
+
`config.default.yaml`. `TypeDBConfig.__repr__` now redacts
|
|
72
|
+
non-empty passwords as `***`. The config loader resolves
|
|
73
|
+
`${TYPEDB_USERNAME}` / `${TYPEDB_PASSWORD}` env-var references in
|
|
74
|
+
YAML (same pattern already used for `llm.api_key`), so secrets can
|
|
75
|
+
stay in env / container secret stores rather than on disk.
|
|
76
|
+
Migration: set `TYPEDB_USERNAME` / `TYPEDB_PASSWORD` in your
|
|
77
|
+
environment or use the `${VAR}` references in a local
|
|
78
|
+
`config.yaml`. Direct env overrides (`TYPEDB_USERNAME=…`) already
|
|
79
|
+
worked and are unaffected.
|
|
80
|
+
|
|
81
|
+
### Docs
|
|
82
|
+
|
|
83
|
+
- **Architecture Deep Dive + Module Inventory for v2.4.0** (#80) —
|
|
84
|
+
reference-level architecture documentation.
|
|
85
|
+
- **RFC-007 Operational Telemetry** (#85) — full design doc including
|
|
86
|
+
the four subagent-resolved frictions (caller-opt-in query_id
|
|
87
|
+
correlation, narrow-scope latency instrumentation, OCSF unmapped
|
|
88
|
+
extension, hybrid `__new__`-bypass integration tests).
|
|
89
|
+
- **Human Evaluation Rubric** (#85) — 6-question monthly review
|
|
90
|
+
rubric with scoring summary table.
|
|
91
|
+
- **Troubleshoot guide** (#85) — "Operational telemetry" subsection
|
|
92
|
+
covering the three CLI entry points and the privacy contract.
|
|
93
|
+
|
|
94
|
+
## [2.4.0] - 2026-04-19
|
|
95
|
+
|
|
96
|
+
Detection-rules-as-memory, MCP Registry publication, SQLite concurrency
|
|
97
|
+
hardening, and a full test-suite hygiene pass.
|
|
98
|
+
|
|
99
|
+
### Added
|
|
100
|
+
|
|
101
|
+
- **Detection rules as first-class memory** (#70) — Sigma and YARA rules
|
|
102
|
+
are now ingested, indexed, and retrieved alongside CTI entities, with
|
|
103
|
+
an LLM rule explainer that surfaces what each rule detects and the
|
|
104
|
+
actors/techniques it's associated with. See the "Detection Rules as
|
|
105
|
+
Memory" section in the README (#74) for usage.
|
|
106
|
+
- **MCP Registry publication** (#75) — `server.json` and the `mcp-name`
|
|
107
|
+
tag required to publish ZettelForge to the canonical MCP Registry
|
|
108
|
+
(registry.modelcontextprotocol.io), which feeds mcp.so and the
|
|
109
|
+
modelcontextprotocol.io community-servers list.
|
|
110
|
+
- **Brand & docs polish** (#61) — neural-chain architecture diagram with
|
|
111
|
+
light/dark parity, updated GitHub social preview, canonical security
|
|
112
|
+
channels + RFC 9116 `security.txt`, real Code of Conduct contacts,
|
|
113
|
+
and a complete brand documentation set.
|
|
114
|
+
|
|
115
|
+
### Fixed
|
|
116
|
+
|
|
117
|
+
- **SQLite backend concurrency** (#69) — 16 reader methods in
|
|
118
|
+
`SQLiteBackend` (`get_note_by_id`, `get_note_by_source_ref`,
|
|
119
|
+
`iterate_notes`, `get_notes_by_domain`, `get_recent_notes`,
|
|
120
|
+
`count_notes`, `get_kg_node`, `get_kg_node_by_id`,
|
|
121
|
+
`get_kg_neighbors`, `traverse_kg`, `get_entity_timeline`,
|
|
122
|
+
`get_changes_since`, `get_causal_edges`, `get_incoming_causal`,
|
|
123
|
+
`get_note_ids_for_entity`, `search_entities`) were executing
|
|
124
|
+
`SELECT` statements without holding `_write_lock`, while writers
|
|
125
|
+
acquired it. Under concurrent background enrichment, readers could
|
|
126
|
+
observe a partially-written row and raise `pydantic.ValidationError`
|
|
127
|
+
on NULL columns. Each reader now wraps its SQL execute+fetch block
|
|
128
|
+
in `with self._write_lock:` (RLock, reentrant-safe). Closes #68.
|
|
129
|
+
Eliminates the `test_apply_delete_marks_superseded` flake and
|
|
130
|
+
prevents the same race from surfacing in production
|
|
131
|
+
`recall()`-during-write paths.
|
|
132
|
+
- **CI regressions** (#67) — stabilized three tests exposed by the
|
|
133
|
+
test-suite audit sprint.
|
|
134
|
+
|
|
135
|
+
### Changed
|
|
136
|
+
|
|
137
|
+
- **Test suite hygiene** (#62, #63, #64, #65) — post-v2.3.0 audit (see
|
|
138
|
+
`docs/superpowers/research/2026-04-17-test-suite-audit.md`)
|
|
139
|
+
converted 10 CI-skipped LLM tests to the mock provider (RFC-002
|
|
140
|
+
Phase 1), resolved both remaining `xfail` tests via prompt-routed
|
|
141
|
+
mocks, eliminated two long-standing flakes
|
|
142
|
+
(`test_recall_cve_returns_notes`, `test_apply_delete_marks_superseded`),
|
|
143
|
+
prepped `langchain_retriever` for Pydantic V3 by migrating to
|
|
144
|
+
`ConfigDict`, and reinstated meaningful causal-edge validation via
|
|
145
|
+
mock-seeded triples + `SQLiteBackend.get_causal_edges` query. Net
|
|
146
|
+
test-suite delta: 280 passed / 17 skipped / 2 xfailed → 305 passed
|
|
147
|
+
/ 10 skipped / 0 xfailed on test-3.12.
|
|
148
|
+
|
|
9
149
|
## [2.3.0] - 2026-04-17
|
|
10
150
|
|
|
11
151
|
Pluggable LLM provider infrastructure (RFC-002 Phase 1), MCP server
|
|
@@ -37,7 +37,7 @@ This Code of Conduct applies within all community spaces, and also applies when
|
|
|
37
37
|
|
|
38
38
|
## Enforcement
|
|
39
39
|
|
|
40
|
-
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [
|
|
40
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement by email at **conduct@threatrecall.ai**, or privately via GitHub's [report abuse](https://github.com/rolandpg/zettelforge/security/advisories/new) flow. All complaints will be reviewed and investigated promptly and fairly.
|
|
41
41
|
|
|
42
42
|
All community leaders are obligated to respect the privacy and security of the reporter of any incident.
|
|
43
43
|
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: zettelforge
|
|
3
|
+
Version: 2.4.1
|
|
4
|
+
Summary: ZettelForge: Agentic Memory System with vector search, knowledge graph, and synthesis
|
|
5
|
+
Project-URL: Homepage, https://github.com/rolandpg/zettelforge
|
|
6
|
+
Project-URL: Documentation, https://docs.threatrecall.ai
|
|
7
|
+
Project-URL: Repository, https://github.com/rolandpg/zettelforge
|
|
8
|
+
Project-URL: Issues, https://github.com/rolandpg/zettelforge/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/rolandpg/zettelforge/blob/master/CHANGELOG.md
|
|
10
|
+
Author-email: Patrick Roland <patrick@groland.com>
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: agentic-memory,ai-agent,cti,cybersecurity,knowledge-graph,llm-memory,mcp-server,rag,stix,threat-intelligence
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
23
|
+
Classifier: Topic :: Security
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Requires-Dist: fastembed>=0.8.0
|
|
27
|
+
Requires-Dist: httpx>=0.25.0
|
|
28
|
+
Requires-Dist: jsonschema>=4.0
|
|
29
|
+
Requires-Dist: lancedb>=0.5.0
|
|
30
|
+
Requires-Dist: numpy>=1.24.0
|
|
31
|
+
Requires-Dist: plyara>=2.0
|
|
32
|
+
Requires-Dist: pyarrow>=14.0.0
|
|
33
|
+
Requires-Dist: pydantic>=2.0.0
|
|
34
|
+
Requires-Dist: pyyaml>=6.0
|
|
35
|
+
Requires-Dist: requests>=2.31.0
|
|
36
|
+
Requires-Dist: structlog>=24.0.0
|
|
37
|
+
Requires-Dist: tantivy>=0.11.0
|
|
38
|
+
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: langchain-core>=0.2.0; extra == 'dev'
|
|
40
|
+
Requires-Dist: mypy>=1.0.0; extra == 'dev'
|
|
41
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
42
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
43
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
44
|
+
Requires-Dist: ruff>=0.4.0; extra == 'dev'
|
|
45
|
+
Provides-Extra: extensions
|
|
46
|
+
Requires-Dist: zettelforge-enterprise>=2.1.0; extra == 'extensions'
|
|
47
|
+
Provides-Extra: langchain
|
|
48
|
+
Requires-Dist: langchain-core>=0.2.0; extra == 'langchain'
|
|
49
|
+
Provides-Extra: local
|
|
50
|
+
Requires-Dist: llama-cpp-python>=0.3.0; extra == 'local'
|
|
51
|
+
Provides-Extra: web
|
|
52
|
+
Requires-Dist: fastapi>=0.100.0; extra == 'web'
|
|
53
|
+
Requires-Dist: uvicorn>=0.20.0; extra == 'web'
|
|
54
|
+
Description-Content-Type: text/markdown
|
|
55
|
+
|
|
56
|
+
# ZettelForge
|
|
57
|
+
|
|
58
|
+
<!-- mcp-name: io.github.rolandpg/zettelforge -->
|
|
59
|
+
|
|
60
|
+
**The only agentic memory system built for cyber threat intelligence.**
|
|
61
|
+
|
|
62
|
+
When a senior analyst leaves, two or three years of context walks out with them — customer environments, prior investigations, actor TTPs, false-positive patterns, every hard-won "wait, we've seen this before." ZettelForge is an agentic memory system built so that context stays with the team.
|
|
63
|
+
|
|
64
|
+
It extracts CVEs, threat actors, IOCs, and ATT&CK techniques from analyst notes and threat reports, resolves aliases (APT28 = Fancy Bear = STRONTIUM = Sofacy), builds a STIX 2.1 knowledge graph, and serves every past investigation back to your analysts — and to Claude Code via MCP — in natural language. Runs entirely in-process. No API keys. No cloud. No data leaves the host.
|
|
65
|
+
|
|
66
|
+
[](https://pypi.org/project/zettelforge/)
|
|
67
|
+
[](https://pepy.tech/projects/zettelforge)
|
|
68
|
+
[](https://www.python.org/downloads/)
|
|
69
|
+
[](https://opensource.org/licenses/MIT)
|
|
70
|
+
[](https://github.com/rolandpg/zettelforge/actions)
|
|
71
|
+
|
|
72
|
+
**[⭐ Star](https://github.com/rolandpg/zettelforge) · [📦 `pip install zettelforge`](https://pypi.org/project/zettelforge/) · [📖 Docs](https://docs.threatrecall.ai/) · [🧪 Hosted beta](https://threatrecall.ai)**
|
|
73
|
+
|
|
74
|
+
<p align="center">
|
|
75
|
+
<a href="https://www.buymeacoffee.com/xypher22pr0" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-green.png" alt="Buy Me a Coffee" style="height: 60px !important;width: 217px !important;" ></a>
|
|
76
|
+
</p>
|
|
77
|
+
<p align="center">
|
|
78
|
+
<img src="https://raw.githubusercontent.com/rolandpg/zettelforge/master/docs/assets/demo.gif" width="720" alt="ZettelForge demo — CTI agentic memory in action">
|
|
79
|
+
</p>
|
|
80
|
+
|
|
81
|
+
> If ZettelForge fits a CTI workflow you run, a star is the fastest signal that this category is worth continuing to invest in.
|
|
82
|
+
|
|
83
|
+
## The problem
|
|
84
|
+
|
|
85
|
+
Every SOC loses analysts. When they leave, investigation context, actor attribution, and environment-specific false-positive patterns go with them. Their replacements re-open the same tickets, re-read the same reports, and re-build the same mental models from scratch.
|
|
86
|
+
|
|
87
|
+
General-purpose AI memory systems don't fix this for security teams. They can't tell APT28 from Fancy Bear, don't know that CVE-2024-3094 is the XZ Utils backdoor, can't parse Sigma or YARA, and have no concept of MITRE ATT&CK technique IDs. When a CTI analyst gives them a year of intel reports, they get back fuzzy semantic search over chat history.
|
|
88
|
+
|
|
89
|
+
ZettelForge was built for analysts who think in threat graphs. It extracts CVEs, threat actors, IOCs, and ATT&CK techniques automatically, resolves aliases across naming conventions, builds a knowledge graph with causal relationships, and retrieves memories using intent-aware blended search — all in-process, with no external API dependency.
|
|
90
|
+
|
|
91
|
+
>"Memory augmentation closes 33% of the gap between small and large models on CTI tasks (CTI-REALM, Microsoft 2026)." [1]
|
|
92
|
+
|
|
93
|
+
| Capability | ZettelForge | Mem0 | Graphiti | Cognee |
|
|
94
|
+
|---|---|---|---|---|
|
|
95
|
+
| CTI entity extraction (CVEs, actors, IOCs) | Yes | No | No | No |
|
|
96
|
+
| STIX 2.1 ontology | Yes | No | No | No |
|
|
97
|
+
| Threat actor alias resolution | Yes (APT28 = Fancy Bear) | No | No | No |
|
|
98
|
+
| Knowledge graph with causal triples | Yes | No | Yes | Yes |
|
|
99
|
+
| Intent-classified retrieval (5 types) | Yes | No | No | No |
|
|
100
|
+
| In-process / no external API required | Yes | No | No | No |
|
|
101
|
+
| Audit logs in OCSF schema | Yes | No | No | No |
|
|
102
|
+
| MCP server (Claude Code) | Yes | No | No | No |
|
|
103
|
+
|
|
104
|
+
## Data Pipeline
|
|
105
|
+
<p align="center">
|
|
106
|
+
<picture>
|
|
107
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/rolandpg/zettelforge/master/docs/assets/zettelforge_architecture.svg">
|
|
108
|
+
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/rolandpg/zettelforge/master/docs/assets/zettelforge_architecture-light.svg">
|
|
109
|
+
<img src="https://raw.githubusercontent.com/rolandpg/zettelforge/master/docs/assets/zettelforge_architecture.svg" width="720" alt="ZettelForge architecture — neural recall loop: ingest, enrich, retrieve, synthesize, backed by SQLite + LanceDB">
|
|
110
|
+
</picture>
|
|
111
|
+
</p>
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
## Features
|
|
115
|
+
|
|
116
|
+
**Entity Extraction** — Automatically identifies CVEs, threat actors, IOCs (IPs, domains, hashes, URLs, emails), MITRE ATT&CK techniques, campaigns, intrusion sets, tools, people, locations, and organizations. Regex + LLM NER with STIX 2.1 types throughout.
|
|
117
|
+
|
|
118
|
+
**Knowledge Graph** — Entities become nodes, co-occurrence becomes edges. LLM infers causal triples ("APT28 *uses* Cobalt Strike"). Temporal edges and supersession track how intelligence evolves.
|
|
119
|
+
|
|
120
|
+
**Alias Resolution** — APT28, Fancy Bear, Sofacy, STRONTIUM all resolve to the same actor node. Works automatically on store and recall.
|
|
121
|
+
|
|
122
|
+
**Blended Retrieval** — Vector similarity (768-dim fastembed, ONNX) + graph traversal (BFS over knowledge graph edges), weighted by intent classification. Five intent types: factual, temporal, relational, exploratory, causal.
|
|
123
|
+
|
|
124
|
+
**Memory Evolution** — With `evolve=True`, new intel is compared to existing memory. LLM decides ADD, UPDATE, DELETE, or NOOP. Stale intel gets superseded. Contradictions get resolved. Duplicates get skipped.
|
|
125
|
+
|
|
126
|
+
**RAG Synthesis** — Synthesize answers across all stored memories with `direct_answer` format.
|
|
127
|
+
|
|
128
|
+
**In-process by architecture** — fastembed (ONNX) for embeddings, llama-cpp-python for optional local LLM inference, SQLite + LanceDB for storage, and Ollama on localhost by default. No external API keys are required. Outbound network access may occur on first run when embedding/LLM models are downloaded; after models are preloaded, it can run fully offline (including on air-gapped hosts).
|
|
129
|
+
|
|
130
|
+
**Audit logging in OCSF schema** — Every operation emits a structured event in the Open Cybersecurity Schema Framework format. What you do with the log stream (SIEM, WORM store, nothing) is up to you.
|
|
131
|
+
|
|
132
|
+
## Quick Start
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
pip install zettelforge
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
from zettelforge import MemoryManager
|
|
140
|
+
|
|
141
|
+
mm = MemoryManager()
|
|
142
|
+
|
|
143
|
+
# Store threat intel — entities extracted automatically
|
|
144
|
+
mm.remember("APT28 uses Cobalt Strike for lateral movement via T1021")
|
|
145
|
+
|
|
146
|
+
# Recall with alias resolution
|
|
147
|
+
results = mm.recall("What tools does Fancy Bear use?")
|
|
148
|
+
# Returns the APT28 note (APT28 = Fancy Bear, resolved automatically)
|
|
149
|
+
|
|
150
|
+
# Synthesize across all memories
|
|
151
|
+
answer = mm.synthesize("Summarize known APT28 TTPs")
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
No TypeDB, no Ollama, no Docker — just `pip install`. Embeddings run in-process via fastembed. LLM features (extraction, synthesis) activate when Ollama is available.
|
|
155
|
+
|
|
156
|
+
### With Ollama (enables LLM features)
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
ollama pull qwen2.5:3b && ollama serve
|
|
160
|
+
# ZettelForge auto-detects Ollama for extraction and synthesis
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Memory Evolution
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
# New intel arrives — evolve=True enables memory evolution:
|
|
167
|
+
# LLM extracts facts, compares to existing notes, decides ADD/UPDATE/DELETE/NOOP
|
|
168
|
+
mm.remember(
|
|
169
|
+
"APT28 has shifted tactics. They dropped DROPBEAR and now exploit edge devices.",
|
|
170
|
+
domain="cti",
|
|
171
|
+
evolve=True, # existing APT28 note gets superseded, not duplicated
|
|
172
|
+
)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## How It Works
|
|
176
|
+
|
|
177
|
+
Every `remember()` call triggers a pipeline:
|
|
178
|
+
|
|
179
|
+
1. **Entity Extraction** — regex + LLM NER identifies CVEs, intrusion sets, threat actors, tools, campaigns, ATT&CK techniques, IOCs (IPv4, domain, URL, MD5/SHA1/SHA256, email), people, locations, organizations, events, activities, and temporal references (19 types)
|
|
180
|
+
2. **Knowledge Graph Update** — entities become nodes, co-occurrence becomes edges, LLM infers causal triples
|
|
181
|
+
3. **Vector Embedding** — 768-dim fastembed (ONNX, in-process, 7ms/embed) stored in LanceDB
|
|
182
|
+
4. **Supersession Check** — entity overlap detection marks stale notes as superseded
|
|
183
|
+
5. **Dual-Stream Write** — fast path returns in ~45ms; causal enrichment is deferred to a background worker
|
|
184
|
+
|
|
185
|
+
Every `recall()` call blends two retrieval strategies:
|
|
186
|
+
|
|
187
|
+
1. **Vector similarity** — semantic search over embeddings
|
|
188
|
+
2. **Graph traversal** — BFS over knowledge graph edges, scored by hop distance
|
|
189
|
+
3. **Intent routing** — query classified as factual/temporal/relational/causal/exploratory, weights adjusted per type
|
|
190
|
+
4. **Cross-encoder reranking** — ms-marco-MiniLM reorders final results by relevance
|
|
191
|
+
|
|
192
|
+
## Benchmarks
|
|
193
|
+
|
|
194
|
+
Evaluated against published academic benchmarks:
|
|
195
|
+
|
|
196
|
+
| Benchmark | What it measures | Score |
|
|
197
|
+
|---|---|---|
|
|
198
|
+
| **CTI Retrieval** | Attribution, CVE linkage, multi-hop | **75.0%** |
|
|
199
|
+
| **RAGAS** | Retrieval quality (keyword presence) | **78.1%** |
|
|
200
|
+
| **LOCOMO** (ACL 2024) | Conversational memory recall | **22.0%** *(with Ollama cloud models)* |
|
|
201
|
+
|
|
202
|
+
See the [full benchmark report](benchmarks/BENCHMARK_REPORT.md) for methodology and analysis.
|
|
203
|
+
|
|
204
|
+
## MCP Server (Claude Code)
|
|
205
|
+
|
|
206
|
+
Add ZettelForge as a memory backend for Claude Code:
|
|
207
|
+
|
|
208
|
+
```json
|
|
209
|
+
{
|
|
210
|
+
"mcpServers": {
|
|
211
|
+
"zettelforge": {
|
|
212
|
+
"command": "python3",
|
|
213
|
+
"args": ["-m", "zettelforge.mcp"]
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Your Claude Code agent can now remember and recall threat intelligence across sessions.
|
|
220
|
+
|
|
221
|
+
Exposed tools: `remember`, `recall`, `synthesize`, `entity`, `graph`, `stats`.
|
|
222
|
+
|
|
223
|
+
## Detection Rules as Memory (Sigma + YARA)
|
|
224
|
+
|
|
225
|
+
Sigma and YARA rules are first-class memory primitives. Parse, validate, and ingest a rule and its tags become graph edges: MITRE ATT&CK techniques, CVEs, threat-actor aliases, tools, and malware families resolve against the same ontology as every other note. A shared `DetectionRule` supertype carries `SigmaRule` and `YaraRule` subtypes, so a single rule UUID is addressable across both formats.
|
|
226
|
+
|
|
227
|
+
Sigma rules are validated against the vendored [SigmaHQ JSON schema](https://github.com/SigmaHQ/sigma-specification). YARA rules are parsed with plyara and checked against the [CCCS YARA metadata standard](https://github.com/CybercentreCanada/CCCS-Yara) (tiers: `strict`, `warn`, `non_cccs`). Ingest is idempotent — re-ingesting an unchanged rule returns the original note via a content-hashed `source_ref`.
|
|
228
|
+
|
|
229
|
+
```python
|
|
230
|
+
from zettelforge import MemoryManager
|
|
231
|
+
from zettelforge.sigma import ingest_rule as ingest_sigma
|
|
232
|
+
from zettelforge.yara import ingest_rule as ingest_yara
|
|
233
|
+
|
|
234
|
+
mm = MemoryManager()
|
|
235
|
+
ingest_sigma("rules/proc_creation_win_office_macro.yml", mm)
|
|
236
|
+
ingest_yara("rules/webshell_china_chopper.yar", mm, tier="warn")
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
# Bulk ingest from SigmaHQ or a private rule repo
|
|
241
|
+
python -m zettelforge.sigma.ingest /path/to/sigma/rules/
|
|
242
|
+
python -m zettelforge.yara.ingest /path/to/yara/rules/ --tier warn
|
|
243
|
+
|
|
244
|
+
# CI fixture check — parse + validate, no writes
|
|
245
|
+
python -m zettelforge.sigma.ingest rules/ --dry-run
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
An LLM rule explainer (`zettelforge.detection.explainer.explain`) produces a structured JSON summary — intent, key fields, evasion notes, false-positive hypotheses — for any `DetectionRule`. It runs synchronously on demand in v1; async enrichment-queue wiring is v1.1. Rate-limited via `ZETTELFORGE_EXPLAIN_RPM` (default 60 calls/minute).
|
|
249
|
+
|
|
250
|
+
References: [Sigma spec](https://github.com/SigmaHQ/sigma-specification), [SigmaHQ rules](https://github.com/SigmaHQ/sigma), [CCCS YARA](https://github.com/CybercentreCanada/CCCS-Yara), [YARA docs](https://yara.readthedocs.io).
|
|
251
|
+
|
|
252
|
+
## Integrations
|
|
253
|
+
|
|
254
|
+
### ATHF (Agentic Threat Hunting Framework)
|
|
255
|
+
|
|
256
|
+
Ingest completed [ATHF](https://github.com/Nebulock-Inc/agentic-threat-hunting-framework) hunts into ZettelForge memory. MITRE techniques and IOCs are extracted and linked in the knowledge graph.
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
python examples/athf_bridge.py /path/to/hunts/
|
|
260
|
+
# 12 hunt(s) parsed
|
|
261
|
+
# Ingested 12/12 hunts into ZettelForge
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
See [examples/athf_bridge.py](examples/athf_bridge.py).
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
## Extensions
|
|
268
|
+
|
|
269
|
+
ZettelForge ships a complete agentic memory core. Everything documented above works from a single `pip install`.
|
|
270
|
+
|
|
271
|
+
For teams that want TypeDB-scale graph storage, OpenCTI integration, or multi-tenant deployment, optional extensions are available:
|
|
272
|
+
|
|
273
|
+
| Extension | What it adds |
|
|
274
|
+
|---|---|
|
|
275
|
+
| TypeDB STIX 2.1 backend | Schema-enforced ontology with inference rules |
|
|
276
|
+
| OpenCTI sync | Bi-directional sync with OpenCTI instances |
|
|
277
|
+
| Multi-tenant auth | OAuth/JWT with per-tenant isolation |
|
|
278
|
+
| Sigma rule generation | Detection rules from extracted IOCs |
|
|
279
|
+
|
|
280
|
+
Extensions install separately:
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
pip install zettelforge-enterprise
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
**Hosted (private beta):** [ThreatRecall](https://threatrecall.ai) is the managed SaaS version of ZettelForge with enterprise extensions enabled. Currently accepting waitlist signups and a limited number of design partners.
|
|
287
|
+
|
|
288
|
+
## Configuration
|
|
289
|
+
|
|
290
|
+
| Variable | Default | Description |
|
|
291
|
+
|---|---|---|
|
|
292
|
+
| `AMEM_DATA_DIR` | `~/.amem` | Data directory |
|
|
293
|
+
| `ZETTELFORGE_BACKEND` | `sqlite` | SQLite community backend. TypeDB available via extension. |
|
|
294
|
+
| `ZETTELFORGE_LLM_PROVIDER` | `local` | `local` (llama-cpp) or `ollama` |
|
|
295
|
+
|
|
296
|
+
See [config.default.yaml](config.default.yaml) for all options.
|
|
297
|
+
|
|
298
|
+
## Contributing
|
|
299
|
+
|
|
300
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup.
|
|
301
|
+
|
|
302
|
+
## License
|
|
303
|
+
|
|
304
|
+
MIT — See [LICENSE](LICENSE).
|
|
305
|
+
|
|
306
|
+
## About the author
|
|
307
|
+
|
|
308
|
+
Built by **Patrick Roland** — Director of SOC Services at Summit 7 Systems, where he built the Vigilance MxDR practice from the ground up. Navy nuclear veteran, CISSP, CCP (CMMC 2.0 Professional). [LinkedIn](https://www.linkedin.com/in/patrickgroland/).
|
|
309
|
+
|
|
310
|
+
## Support the Project
|
|
311
|
+
|
|
312
|
+
ZettelForge is MIT-licensed. If it's useful in your workflow and you'd like to help keep it maintained:
|
|
313
|
+
|
|
314
|
+
<a href="https://www.buymeacoffee.com/xypher22pr0" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-green.png" alt="Buy Me a Coffee" style="height: 40px !important;width: 145px !important;" ></a>
|
|
315
|
+
|
|
316
|
+
## Acknowledgments
|
|
317
|
+
|
|
318
|
+
- Inspired by [Zettelkasten](https://en.wikipedia.org/wiki/Zettelkasten) and [A-Mem](https://arxiv.org/abs/2602.10715) (NeurIPS 2025)
|
|
319
|
+
- Two-phase pipeline inspired by [Mem0](https://mem0.ai/research)
|
|
320
|
+
- STIX 2.1 schema informed by [typedb-cti](https://github.com/typedb-osi/typedb-cti)
|
|
321
|
+
- Benchmarked against [LOCOMO](https://snap-research.github.io/locomo/) (ACL 2024) and [CTIBench](https://arxiv.org/abs/2406.07599) (NeurIPS 2024)
|
|
322
|
+
- [LanceDB](https://lancedb.com) | [fastembed](https://github.com/qdrant/fastembed) | [Pydantic](https://pydantic.dev) | [TypeDB](https://typedb.com)
|
|
323
|
+
|
|
324
|
+
[1]: https://www.microsoft.com/en-us/security/blog/2026/03/20/cti-realm-a-new-benchmark-for-end-to-end-detection-rule-generation-with-ai-agents/
|