zettelforge 2.2.0__tar.gz → 2.3.0__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.2.0 → zettelforge-2.3.0}/.github/workflows/ci.yml +2 -1
- {zettelforge-2.2.0 → zettelforge-2.3.0}/.github/workflows/docs.yml +2 -2
- {zettelforge-2.2.0 → zettelforge-2.3.0}/.gitignore +4 -1
- zettelforge-2.3.0/ARCHITECTURE.md +42 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/CHANGELOG.md +143 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/PKG-INFO +37 -33
- {zettelforge-2.2.0 → zettelforge-2.3.0}/README.md +29 -29
- {zettelforge-2.2.0 → zettelforge-2.3.0}/SECURITY.md +17 -11
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/BENCHMARK_REPORT.md +45 -19
- {zettelforge-2.2.0 → zettelforge-2.3.0}/config.default.yaml +49 -17
- zettelforge-2.3.0/docs/architecture-diagram.mmd +202 -0
- zettelforge-2.3.0/docs/archive/README.md +11 -0
- zettelforge-2.3.0/docs/assets/ZettelForge_Architecture.mmd +185 -0
- zettelforge-2.3.0/docs/assets/architecture-overview.mmd +57 -0
- zettelforge-2.3.0/docs/assets/architecture-read-path.mmd +140 -0
- zettelforge-2.3.0/docs/assets/architecture-write-path.mmd +122 -0
- zettelforge-2.3.0/docs/assets/zettelforge_architecture.svg +77 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/explanation/architecture.md +1 -1
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/explanation/stix-in-zettelforge.md +1 -1
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/how-to/integrate-llm-agent.md +1 -1
- zettelforge-2.3.0/docs/how-to/migrate-jsonl-to-sqlite.md +112 -0
- zettelforge-2.3.0/docs/how-to/reproduce-benchmarks.md +120 -0
- zettelforge-2.3.0/docs/how-to/troubleshoot.md +180 -0
- zettelforge-2.3.0/docs/how-to/upgrade.md +94 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/index.md +4 -4
- zettelforge-2.3.0/docs/llms.txt +64 -0
- zettelforge-2.3.0/docs/narrative/2026-04-16-the-memory-problem.md +86 -0
- zettelforge-2.3.0/docs/overrides/main.html +82 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/reference/configuration.md +39 -10
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/reference/governance-controls.md +2 -2
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/reference/memory-manager-api.md +1 -1
- zettelforge-2.3.0/docs/rfcs/RFC-001-conversational-entity-extractor.md +356 -0
- zettelforge-2.3.0/docs/rfcs/RFC-002-universal-llm-provider.md +883 -0
- zettelforge-2.3.0/docs/rfcs/RFC-003-adversarial-review.md +393 -0
- zettelforge-2.3.0/docs/rfcs/RFC-003-read-path-depth-routing.md +936 -0
- {zettelforge-2.2.0/docs/superpowers/plans → zettelforge-2.3.0/docs/superpowers/research}/2026-04-15-causal-graph.md +36 -0
- zettelforge-2.3.0/docs/superpowers/research/2026-04-15-format-stability.md +1029 -0
- {zettelforge-2.2.0/docs/superpowers/plans → zettelforge-2.3.0/docs/superpowers/research}/2026-04-15-memory-evolution.md +41 -1
- zettelforge-2.3.0/docs/superpowers/research/2026-04-15-merge-consolidation.md +372 -0
- {zettelforge-2.2.0/docs/superpowers/plans → zettelforge-2.3.0/docs/superpowers/research}/2026-04-15-persistence-semantics.md +50 -0
- zettelforge-2.3.0/docs/superpowers/research/README.md +40 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/superpowers/specs/2026-04-15-p1-features-prd.md +197 -29
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/tutorials/01-quickstart.md +3 -3
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/tutorials/02-first-cti-report.md +2 -2
- {zettelforge-2.2.0 → zettelforge-2.3.0}/mkdocs.yml +23 -2
- {zettelforge-2.2.0 → zettelforge-2.3.0}/pyproject.toml +12 -4
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/__init__.py +14 -10
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/backend_factory.py +21 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/cache.py +7 -3
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/config.py +117 -6
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/entity_indexer.py +11 -3
- zettelforge-2.3.0/src/zettelforge/llm_client.py +287 -0
- zettelforge-2.3.0/src/zettelforge/llm_providers/__init__.py +84 -0
- zettelforge-2.3.0/src/zettelforge/llm_providers/base.py +59 -0
- zettelforge-2.3.0/src/zettelforge/llm_providers/local_provider.py +96 -0
- zettelforge-2.3.0/src/zettelforge/llm_providers/mock_provider.py +50 -0
- zettelforge-2.3.0/src/zettelforge/llm_providers/ollama_provider.py +56 -0
- zettelforge-2.3.0/src/zettelforge/llm_providers/registry.py +76 -0
- zettelforge-2.3.0/src/zettelforge/mcp/__init__.py +34 -0
- zettelforge-2.3.0/src/zettelforge/mcp/__main__.py +11 -0
- zettelforge-2.2.0/web/mcp_server.py → zettelforge-2.3.0/src/zettelforge/mcp/server.py +30 -40
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/memory_manager.py +107 -3
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/observability.py +8 -3
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/ocsf.py +15 -1
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/sqlite_backend.py +1 -1
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/vector_retriever.py +12 -2
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_core.py +4 -3
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_langchain_retriever.py +13 -6
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_llm_client.py +40 -20
- zettelforge-2.3.0/tests/test_llm_providers.py +318 -0
- zettelforge-2.3.0/tests/test_mcp_server.py +139 -0
- zettelforge-2.3.0/web/mcp_server.py +18 -0
- zettelforge-2.2.0/ARCHITECTURE.md +0 -36
- zettelforge-2.2.0/docs/llms.txt +0 -50
- zettelforge-2.2.0/docs/rfcs/RFC-001-conversational-entity-extractor.md +0 -113
- zettelforge-2.2.0/src/zettelforge/llm_client.py +0 -150
- {zettelforge-2.2.0 → zettelforge-2.3.0}/.github/CODEOWNERS +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/.github/SECURITY.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/.github/dependabot.yml +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/.github/pull_request_template.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/.github/workflows/publish.yml +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/.github/workflows/snyk-security.yml +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/CODEOWNERS +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/CODE_OF_CONDUCT.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/CONTRIBUTING.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/Dockerfile +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/GOVERNANCE.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/LICENSE +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/MANIFEST.in +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/LOCOMO_BENCHMARK_COMPARISON.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/auto_ralph.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/benchmark_harness.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/cti_benchmark_v2.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/cti_retrieval_benchmark.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/cti_retrieval_results.json +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/cti_v2_results.json +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/ctibench_benchmark.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/ctibench_results.json +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/dataset.json +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/enterprise-attack.json +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/evolve_benchmark.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/evolve_results.json +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/graph_test.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/locomo_benchmark.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/locomo_results.json +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/locomo_results_v1.3.0_baseline.json +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/memoryagentbench.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/memoryagentbench_results.json +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/mempalace_benchmark.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/mempalace_results.json +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/naive_memory.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/opencti_benchmark.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/ragas_benchmark.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/ragas_cti_results.json +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/ragas_results.json +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/results/benchmark_report.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/results/ralph_optimization_log.json +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/scale_benchmark.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/benchmarks/scale_results.json +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/config.example.yaml +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docker/docker-compose.yml +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/CNAME +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0/docs/archive}/PACKAGE_SUMMARY.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0/docs/archive}/SKILL.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/assets/demo.gif +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/assets/favicon.svg +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/assets/logo.svg +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/assets/social-preview.png +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/assets/threatrecall-logo-philosophy.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/assets/threatrecall-logo.png +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/assets/threatrecall-logo.svg +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/assets/threatrecall-mark.png +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/assets/threatrecall-mark.svg +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/explanation/epistemic-tiers.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/explanation/two-phase-pipeline.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/explanation/zettelkasten-philosophy.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/how-to/configure-lancedb.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/how-to/configure-opencti.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/how-to/configure-typedb.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/how-to/ingest-news-report.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/how-to/query-apt-tools.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/how-to/resolve-aliases.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/how-to/run-temporal-query.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/how-to/store-threat-actor.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/reference/retrieval-policies.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/reference/stix-schema.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/docs/stylesheets/extra.css +0 -0
- {zettelforge-2.2.0/docs/superpowers/plans → zettelforge-2.3.0/docs/superpowers/research}/2026-04-09-ctibench-ragas-benchmarks.md +0 -0
- {zettelforge-2.2.0/docs/superpowers/plans → zettelforge-2.3.0/docs/superpowers/research}/2026-04-09-fastembed-local-embeddings.md +0 -0
- {zettelforge-2.2.0/docs/superpowers/plans → zettelforge-2.3.0/docs/superpowers/research}/2026-04-09-hybrid-typedb-lancedb-architecture.md +0 -0
- {zettelforge-2.2.0/docs/superpowers/plans → zettelforge-2.3.0/docs/superpowers/research}/2026-04-09-local-llm-llama-cpp.md +0 -0
- {zettelforge-2.2.0/docs/superpowers/plans → zettelforge-2.3.0/docs/superpowers/research}/2026-04-15-anti-aversion-cleanup.md +0 -0
- {zettelforge-2.2.0/docs/superpowers/plans → zettelforge-2.3.0/docs/superpowers/research}/2026-04-15-ctibench-ate-fix.md +0 -0
- {zettelforge-2.2.0/docs/superpowers/plans → zettelforge-2.3.0/docs/superpowers/research}/2026-04-15-sqlite-migration.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/examples/athf_bridge.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/examples/mcp_claude_code.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/examples/quickstart.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/governance/controls.yaml +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/scripts/migrate_jsonl_to_sqlite.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/scripts/rebuild_index.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/scripts/record-demo.sh +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/scripts/typedb-setup.sh +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/scripts/zettelforge-rebuild.service +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/scripts/zettelforge-rebuild.timer +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/skills/claude-code-skill.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/skills/openclaw-skill.md +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/__main__.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/alias_resolver.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/blended_retriever.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/consolidation.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/demo.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/edition.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/extensions.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/fact_extractor.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/governance_validator.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/graph_retriever.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/integrations/__init__.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/integrations/langchain_retriever.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/intent_classifier.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/json_parse.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/knowledge_graph.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/log.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/memory_evolver.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/memory_store.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/memory_updater.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/note_constructor.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/note_schema.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/ontology.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/retry.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/storage_backend.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/synthesis_generator.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/synthesis_validator.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/src/zettelforge/vector_memory.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/__init__.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/benchmark_scale.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/conftest.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_basic.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_blended_retriever.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_causal_extraction.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_config.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_consolidation.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_conversational_entities.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_cti_integration.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_edition.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_embedding.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_extensions.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_fact_extractor.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_governance.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_governance_spec_drift.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_graph_retriever.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_intent_classifier.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_json_parse.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_logging_compliance.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_memory_evolver.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_memory_updater.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_performance.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_recall_integration.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_sqlite_backend.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_sqlite_integration.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_storage_backend.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_temporal_graph.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_two_phase_e2e.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/tests/test_typedb_client.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/web/app.py +0 -0
- {zettelforge-2.2.0 → zettelforge-2.3.0}/web/auth.py +0 -0
|
@@ -14,8 +14,8 @@ jobs:
|
|
|
14
14
|
deploy:
|
|
15
15
|
runs-on: ubuntu-latest
|
|
16
16
|
steps:
|
|
17
|
-
- uses: actions/checkout@
|
|
18
|
-
- uses: actions/setup-python@
|
|
17
|
+
- uses: actions/checkout@v6
|
|
18
|
+
- uses: actions/setup-python@v6
|
|
19
19
|
with:
|
|
20
20
|
python-version: '3.12'
|
|
21
21
|
- run: pip install mkdocs-material
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
config.yaml
|
|
3
3
|
config.yml
|
|
4
4
|
.env
|
|
5
|
+
.env.*
|
|
6
|
+
*.key
|
|
7
|
+
*.pem
|
|
5
8
|
|
|
6
9
|
# Local planning & task tracking (not committed)
|
|
7
10
|
TODO.md
|
|
@@ -62,7 +65,7 @@ htmlcov/
|
|
|
62
65
|
vectordb/
|
|
63
66
|
.vector_memory.lance/
|
|
64
67
|
.snapshots/
|
|
65
|
-
archive/
|
|
68
|
+
/archive/
|
|
66
69
|
|
|
67
70
|
# Data directories (runtime, not source)
|
|
68
71
|
/data/
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
Visual diagram: [`docs/architecture-diagram.mmd`](docs/architecture-diagram.mmd)
|
|
4
|
+
Deep explanation: [`docs/explanation/architecture.md`](docs/explanation/architecture.md)
|
|
5
|
+
|
|
6
|
+
## Storage
|
|
7
|
+
|
|
8
|
+
ZettelForge uses a `StorageBackend` ABC (33 methods) with pluggable
|
|
9
|
+
implementations. Set `ZETTELFORGE_BACKEND` to select.
|
|
10
|
+
|
|
11
|
+
- **SQLite** (default since v2.2.0): WAL mode, ACID, zero-config.
|
|
12
|
+
Notes, knowledge graph, and entity index in one database file.
|
|
13
|
+
- **LanceDB**: Vector index alongside SQLite. 768-dim embeddings
|
|
14
|
+
via fastembed (nomic-embed-text-v1.5-Q, ONNX, in-process).
|
|
15
|
+
|
|
16
|
+
## Core Pipeline
|
|
17
|
+
|
|
18
|
+
1. **Ingestion** — Governance validation → Note construction → Entity
|
|
19
|
+
extraction (regex fast-path + LLM) → Alias resolution → Storage
|
|
20
|
+
2. **Enrichment** (background) — Causal triple extraction, memory
|
|
21
|
+
evolution (A-Mem neighbor refinement), HGAM consolidation
|
|
22
|
+
3. **Retrieval** — Intent classification → Blended vector + graph
|
|
23
|
+
search → Temporal/causal boosting → Cross-encoder reranking
|
|
24
|
+
4. **Synthesis** — RAG-as-Answer with quality validation
|
|
25
|
+
|
|
26
|
+
## Extension Points
|
|
27
|
+
|
|
28
|
+
Extensions are optional packages discovered at startup via
|
|
29
|
+
`src/zettelforge/extensions.py`. If installed, they provide
|
|
30
|
+
alternative backends and integrations.
|
|
31
|
+
|
|
32
|
+
- Knowledge graph: TypeDB STIX 2.1 ontology with inference rules
|
|
33
|
+
- Authentication: Multi-tenant OAuth/JWT
|
|
34
|
+
- Integrations: OpenCTI bi-directional sync, Sigma rule generation
|
|
35
|
+
|
|
36
|
+
## Why These Boundaries
|
|
37
|
+
|
|
38
|
+
TypeDB requires a running server. OpenCTI is a complex platform.
|
|
39
|
+
These dependencies should not be required to try ZettelForge.
|
|
40
|
+
|
|
41
|
+
The default backends are not toy implementations — they are
|
|
42
|
+
production-capable for single-user and small-team deployments.
|
|
@@ -6,6 +6,149 @@ Versioning follows [Semantic Versioning](https://semver.org/).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [2.3.0] - 2026-04-17
|
|
10
|
+
|
|
11
|
+
Pluggable LLM provider infrastructure (RFC-002 Phase 1), MCP server
|
|
12
|
+
as a first-class Python module, PyPI discoverability refresh, SEO
|
|
13
|
+
foundations across the docs site, and a full docs-vs-code
|
|
14
|
+
reconciliation. All additions are backward-compatible; no existing
|
|
15
|
+
API changes. Supersedes the never-tagged 2.2.1 metadata patch —
|
|
16
|
+
its PyPI classifier / keyword / image-URL changes are folded in
|
|
17
|
+
below.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- **Pluggable LLM provider infrastructure (RFC-002 Phase 1)** — new
|
|
22
|
+
`zettelforge.llm_providers` package with a `@runtime_checkable`
|
|
23
|
+
`LLMProvider` protocol, a thread-safe registry, and built-in
|
|
24
|
+
providers for `local` (llama-cpp-python), `ollama`, and `mock`.
|
|
25
|
+
The public `generate()` signature is unchanged; all 7 existing call
|
|
26
|
+
sites (`fact_extractor`, `memory_updater`, `synthesis_generator`,
|
|
27
|
+
`intent_classifier`, `note_constructor`, `entity_indexer`,
|
|
28
|
+
`memory_evolver`) keep working without modification. Third-party
|
|
29
|
+
providers can register via the `zettelforge.llm_providers`
|
|
30
|
+
entry-point group. `openai_compat` and `anthropic` providers land
|
|
31
|
+
in Phase 2 and Phase 3.
|
|
32
|
+
- **`LLMConfig` expanded** — new `api_key`, `timeout`, `max_retries`,
|
|
33
|
+
`fallback`, and `extra` fields. `api_key` supports `${ENV_VAR}`
|
|
34
|
+
references and is redacted from `repr()`. Sensitive keys inside
|
|
35
|
+
`extra` (matching `key|token|secret|password|credential|auth`) are
|
|
36
|
+
redacted as well. New env overrides: `ZETTELFORGE_LLM_API_KEY`,
|
|
37
|
+
`ZETTELFORGE_LLM_TIMEOUT`, `ZETTELFORGE_LLM_MAX_RETRIES`,
|
|
38
|
+
`ZETTELFORGE_LLM_FALLBACK`.
|
|
39
|
+
- **`LLMProviderConfigurationError`** — new exception surfaced for
|
|
40
|
+
non-recoverable provider setup problems (bad API key, missing
|
|
41
|
+
optional SDK) so `generate()` can distinguish "try the fallback"
|
|
42
|
+
from "stop and report".
|
|
43
|
+
- **`llm_client.reload()` helper** — clears the provider registry
|
|
44
|
+
and config cache so test suites and long-lived processes can
|
|
45
|
+
reconfigure the LLM backend without a process restart.
|
|
46
|
+
- **Hardened .gitignore** per GOV-023 — added `.env.*`, `*.key`,
|
|
47
|
+
`*.pem`.
|
|
48
|
+
- **MCP server as a first-class module** — `python -m zettelforge.mcp`
|
|
49
|
+
now works out of a `pip install zettelforge` with no git clone
|
|
50
|
+
required. New package `zettelforge.mcp` (with `server.py`,
|
|
51
|
+
`__main__.py`, and a console-script entry `zettelforge-mcp`).
|
|
52
|
+
The previous entry point at `web/mcp_server.py` is retained as a
|
|
53
|
+
thin backward-compat shim.
|
|
54
|
+
- **Console scripts** — `zettelforge` and `zettelforge-mcp` entry
|
|
55
|
+
points added to `pyproject.toml`.
|
|
56
|
+
- **How-to guides** — migration (`migrate-jsonl-to-sqlite.md`),
|
|
57
|
+
benchmark reproduction (`reproduce-benchmarks.md`), troubleshooting
|
|
58
|
+
(`troubleshoot.md`), and upgrade (`upgrade.md`). Linked from the
|
|
59
|
+
MkDocs nav.
|
|
60
|
+
- **Design and About sections in the docs nav** — RFC-001, RFC-002,
|
|
61
|
+
RFC-003 and the origin-story narrative are now discoverable from
|
|
62
|
+
`docs.threatrecall.ai`.
|
|
63
|
+
- **RFC-003 design proposal (docs only)** — read-path depth routing
|
|
64
|
+
with a deterministic Quality Gate plus System 1 / System 2 recall
|
|
65
|
+
paths. Ships with an adversarial-review artifact (4 blockers, 13
|
|
66
|
+
warnings). No runtime changes yet — implementation deferred.
|
|
67
|
+
- **Archive directory** — `docs/archive/` holds retired v1.0.0-alpha
|
|
68
|
+
snapshots (`SKILL.md`, `PACKAGE_SUMMARY.md`) with a README explaining
|
|
69
|
+
their provenance.
|
|
70
|
+
- **`llm_ner` configuration reference** — `docs/reference/configuration.md`
|
|
71
|
+
now documents `llm_ner.enabled` and the `ZETTELFORGE_LLM_NER_ENABLED`
|
|
72
|
+
environment override.
|
|
73
|
+
- **Docs SEO foundation** — per-page canonical URLs, OpenGraph and
|
|
74
|
+
Twitter-card metadata, and a `SoftwareApplication` JSON-LD block on
|
|
75
|
+
the home page via a `docs/overrides/main.html` theme override. The
|
|
76
|
+
`softwareVersion` value is sourced from `config.extra.version` in
|
|
77
|
+
`mkdocs.yml` so it stays in sync with releases.
|
|
78
|
+
- **PyPI classifier refresh** — added `Topic :: Security` (primary
|
|
79
|
+
filter security engineers use to browse PyPI) and
|
|
80
|
+
`Topic :: Software Development :: Libraries :: Python Modules`.
|
|
81
|
+
Existing `Topic :: Scientific/Engineering :: Artificial Intelligence`
|
|
82
|
+
retained. Development Status stays at `4 - Beta`.
|
|
83
|
+
- **PyPI keyword refresh** — swapped `agent-memory` → `agentic-memory`
|
|
84
|
+
(emerging category keyword) and `zettelkasten` → `llm-memory`
|
|
85
|
+
(direct intent match for Mem0/Graphiti discovery traffic). Still
|
|
86
|
+
10 keywords total; within the PyPI display limit.
|
|
87
|
+
|
|
88
|
+
### Changed
|
|
89
|
+
|
|
90
|
+
- **SECURITY.md** — contact updated to `contact@threatrecall.ai`,
|
|
91
|
+
supported-versions table refreshed to mark `2.3.x` as current and
|
|
92
|
+
`2.2.x` as the prior minor release; storage section refreshed to
|
|
93
|
+
reflect SQLite-by-default.
|
|
94
|
+
- **`docs/llms.txt`** — rewritten to match current reality (SQLite
|
|
95
|
+
default, 19 runtime entity types, correct GOV-003/007/011/012
|
|
96
|
+
descriptions, MCP invocation).
|
|
97
|
+
- **BENCHMARK_REPORT.md** — CTIBench ATE row updated (F1 = 0.146);
|
|
98
|
+
architecture summary reframed as SQLite + LanceDB default with
|
|
99
|
+
TypeDB as an extension; `ctibench_results.json` date bumped.
|
|
100
|
+
- **README** — above-fold rewritten (CTA row, keyword density,
|
|
101
|
+
PyPI-safe absolute-URL images). Pipeline step 1 entity count
|
|
102
|
+
corrected from "10 types" to the 19 types `EntityExtractor`
|
|
103
|
+
actually recognises.
|
|
104
|
+
- **README image paths** — `docs/assets/demo.gif` and
|
|
105
|
+
`docs/assets/zettelforge_architecture.svg` rewritten to absolute
|
|
106
|
+
`raw.githubusercontent.com` URLs so the PyPI long description
|
|
107
|
+
renders correctly (relative paths 404 on the PyPI CDN). Pinned to
|
|
108
|
+
the `master` ref; can be re-pinned to the `v2.3.0` tag in the
|
|
109
|
+
next release PR if PyPI-side stability matters.
|
|
110
|
+
- **`docs/superpowers/plans/` renamed to `docs/superpowers/research/`**
|
|
111
|
+
with a README making clear these are aspirational synthesis, not
|
|
112
|
+
roadmap commitments. The stray untracked `docs/plans/` directory
|
|
113
|
+
was removed.
|
|
114
|
+
- **Tutorials and governance-controls reference** — `last_updated`
|
|
115
|
+
and `version` metadata refreshed.
|
|
116
|
+
- **`zettelforge.ontology` exports** — `TypedEntityStore`,
|
|
117
|
+
`OntologyValidator`, `get_ontology_store`, `get_ontology_validator`
|
|
118
|
+
removed from the top-level `__all__` (still importable from
|
|
119
|
+
`zettelforge.ontology`). They are a parallel store not wired into
|
|
120
|
+
`MemoryManager`.
|
|
121
|
+
- **`observability.py` and `cache.py` headers** — annotated as
|
|
122
|
+
currently unwired; kept for future integration.
|
|
123
|
+
- **OCSF `_PRODUCT_VERSION`** — sourced from
|
|
124
|
+
`importlib.metadata.version("zettelforge")` instead of a hard-coded
|
|
125
|
+
string, so emitted OCSF events stop drifting when `__version__`
|
|
126
|
+
bumps.
|
|
127
|
+
- **OpenGraph `og:type`** — `website` on the home page, `article`
|
|
128
|
+
elsewhere (was unconditionally `article`).
|
|
129
|
+
|
|
130
|
+
### Fixed
|
|
131
|
+
|
|
132
|
+
- **OllamaProvider host routing** — now instantiates
|
|
133
|
+
`ollama.Client(host=self._url)` so the configured URL actually
|
|
134
|
+
takes effect (previously the module-level `ollama.generate()` call
|
|
135
|
+
ignored per-instance host).
|
|
136
|
+
- **Provider registry race** — `register()` now checks and mutates
|
|
137
|
+
under the registry lock, closing a TOCTOU window on concurrent
|
|
138
|
+
provider registration.
|
|
139
|
+
- **MCP server lazy instantiation** — `MemoryManager` is now created
|
|
140
|
+
on first tool call rather than at server import time, so `--help`
|
|
141
|
+
and protocol-handshake tests don't pay the model-load cost.
|
|
142
|
+
|
|
143
|
+
### Removed
|
|
144
|
+
|
|
145
|
+
- Six superseded branches that had already been squash-merged into
|
|
146
|
+
master — `feat/causal-chain-fix-and-demo-gif`,
|
|
147
|
+
`feat/entity-vocabulary-expansion`,
|
|
148
|
+
`feature/RFC-001-conversational-entity-extractor`,
|
|
149
|
+
`fix/intent-classifier-graph-weight`,
|
|
150
|
+
`fix/p0-production-blockers`, `feat/remember-evolve`.
|
|
151
|
+
|
|
9
152
|
## [2.2.0] - 2026-04-16
|
|
10
153
|
|
|
11
154
|
SQLite default backend, causal chain retrieval, memory evolution, STIX taxonomy alignment, and community-first package cleanup.
|
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: zettelforge
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: ZettelForge: Agentic Memory System with vector search, knowledge graph, and synthesis
|
|
5
5
|
Project-URL: Homepage, https://github.com/rolandpg/zettelforge
|
|
6
|
-
Project-URL: Documentation, https://
|
|
6
|
+
Project-URL: Documentation, https://docs.threatrecall.ai
|
|
7
7
|
Project-URL: Repository, https://github.com/rolandpg/zettelforge
|
|
8
8
|
Project-URL: Issues, https://github.com/rolandpg/zettelforge/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/rolandpg/zettelforge/blob/master/CHANGELOG.md
|
|
9
10
|
Author-email: Patrick Roland <patrick@groland.com>
|
|
10
11
|
License-Expression: MIT
|
|
11
12
|
License-File: LICENSE
|
|
12
|
-
Keywords:
|
|
13
|
-
Classifier: Development Status ::
|
|
13
|
+
Keywords: agentic-memory,ai-agent,cti,cybersecurity,knowledge-graph,llm-memory,mcp-server,rag,stix,threat-intelligence
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
15
|
Classifier: Intended Audience :: Developers
|
|
15
16
|
Classifier: License :: OSI Approved :: MIT License
|
|
16
17
|
Classifier: Programming Language :: Python :: 3
|
|
17
18
|
Classifier: Programming Language :: Python :: 3.10
|
|
18
19
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
20
|
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
22
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
23
|
+
Classifier: Topic :: Security
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
25
|
Requires-Python: >=3.10
|
|
22
26
|
Requires-Dist: fastembed>=0.8.0
|
|
23
27
|
Requires-Dist: httpx>=0.25.0
|
|
@@ -50,28 +54,34 @@ Description-Content-Type: text/markdown
|
|
|
50
54
|
|
|
51
55
|
**The only agentic memory system built for cyber threat intelligence.**
|
|
52
56
|
|
|
53
|
-
|
|
57
|
+
Persistent memory for AI agents and Claude Code — with CTI entity extraction, STIX knowledge graphs, threat-actor alias resolution, and offline-first RAG. MCP server included. No cloud, no API keys.
|
|
54
58
|
|
|
55
59
|
[](https://pypi.org/project/zettelforge/)
|
|
56
|
-
[](https://pepy.tech/projects/zettelforge)
|
|
57
|
-
[](https://github.com/rolandpg/zettelforge/stargazers)
|
|
58
|
-
[](https://opensource.org/licenses/MIT)
|
|
60
|
+
[](https://pepy.tech/projects/zettelforge)
|
|
59
61
|
[](https://www.python.org/downloads/)
|
|
62
|
+
[](https://opensource.org/licenses/MIT)
|
|
60
63
|
[](https://github.com/rolandpg/zettelforge/actions)
|
|
61
|
-
[](https://github.com/rolandpg/zettelforge/graphs/contributors)
|
|
62
|
-
[](https://github.com/rolandpg/zettelforge/commits/master)
|
|
63
|
-
[](https://safeskill.dev/scan/rolandpg-zettelforge)
|
|
64
64
|
|
|
65
|
+
**[⭐ Star](https://github.com/rolandpg/zettelforge) · [📦 `pip install zettelforge`](https://pypi.org/project/zettelforge/) · [📖 Docs](https://docs.threatrecall.ai/) · [🧪 Hosted](https://threatrecall.ai)**
|
|
66
|
+
|
|
67
|
+
<p align="center">
|
|
68
|
+
<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>
|
|
69
|
+
</p>
|
|
65
70
|
<p align="center">
|
|
66
|
-
<img src="docs/assets/demo.gif" width="720" alt="ZettelForge demo — CTI agentic memory in action">
|
|
71
|
+
<img src="https://raw.githubusercontent.com/rolandpg/zettelforge/master/docs/assets/demo.gif" width="720" alt="ZettelForge demo — CTI agentic memory in action">
|
|
67
72
|
</p>
|
|
68
73
|
|
|
74
|
+
> If ZettelForge fits a CTI workflow you run, a star is the fastest signal that this category is worth continuing to invest in.
|
|
75
|
+
|
|
69
76
|
## Why ZettelForge?
|
|
70
77
|
|
|
71
78
|
General-purpose memory systems don't understand threat intelligence. They can't tell APT28 from Fancy Bear, don't know that CVE-2024-3094 is the XZ Utils backdoor, and can't track how intelligence evolves across reports. When your agent forgets context between investigations, you end up re-reading the same reports and re-building the same mental models.
|
|
72
79
|
|
|
73
80
|
ZettelForge was built from the ground up for analysts who think in threat graphs, not chat histories. It extracts CVEs, threat actors, IOCs, and MITRE 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 offline, with no API keys or cloud dependencies.
|
|
74
81
|
|
|
82
|
+
>"Memory augmentation closes 33% of the gap between small and large models on CTI tasks (CTI-REALM, Microsoft 2026)." [1]
|
|
83
|
+
|
|
84
|
+
|
|
75
85
|
| Feature | ZettelForge | Mem0 | Graphiti | Cognee |
|
|
76
86
|
|---------|------------|------|----------|--------|
|
|
77
87
|
| CTI entity extraction (CVEs, actors, IOCs) | Yes | No | No | No |
|
|
@@ -83,6 +93,12 @@ ZettelForge was built from the ground up for analysts who think in threat graphs
|
|
|
83
93
|
| OCSF audit logging | Yes | No | No | No |
|
|
84
94
|
| MCP server (Claude Code) | Yes | No | No | No |
|
|
85
95
|
|
|
96
|
+
## Data Pipeline
|
|
97
|
+
<p align="center">
|
|
98
|
+
<img src="https://raw.githubusercontent.com/rolandpg/zettelforge/master/docs/assets/zettelforge_architecture.svg" width="720" alt="ZettelForge architecture — Extract, Graph, Embed, Recall pipeline">
|
|
99
|
+
</p>
|
|
100
|
+
|
|
101
|
+
|
|
86
102
|
## Features
|
|
87
103
|
|
|
88
104
|
**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.
|
|
@@ -148,7 +164,7 @@ mm.remember(
|
|
|
148
164
|
|
|
149
165
|
Every `remember()` call triggers a pipeline:
|
|
150
166
|
|
|
151
|
-
1. **Entity Extraction** -- regex + LLM NER identifies CVEs, actors, tools, campaigns, people, locations,
|
|
167
|
+
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)
|
|
152
168
|
2. **Knowledge Graph Update** -- entities become nodes, co-occurrence becomes edges, LLM infers causal triples
|
|
153
169
|
3. **Vector Embedding** -- 768-dim fastembed (ONNX, in-process, 7ms/embed) stored in LanceDB
|
|
154
170
|
4. **Supersession Check** -- entity overlap detection marks stale notes as superseded
|
|
@@ -206,26 +222,6 @@ python examples/athf_bridge.py /path/to/hunts/
|
|
|
206
222
|
|
|
207
223
|
See [examples/athf_bridge.py](examples/athf_bridge.py).
|
|
208
224
|
|
|
209
|
-
## Architecture
|
|
210
|
-
|
|
211
|
-
```
|
|
212
|
-
┌──────────────────────────────────────────────────────────────────────┐
|
|
213
|
-
│ MemoryManager │
|
|
214
|
-
│ remember() remember_with_extraction() recall() synthesize() │
|
|
215
|
-
├──────────┬───────────┬──────────────┬───────────┬────────────────────┤
|
|
216
|
-
│ Note │ Fact │ Memory │ Blended │ Synthesis │
|
|
217
|
-
│Constructor│ Extractor │ Updater │ Retriever │ Generator │
|
|
218
|
-
│(enrich) │(Phase 1) │(Phase 2) │(vec+graph)│ (RAG) │
|
|
219
|
-
├──────────┴───────────┴──────────────┼───────────┴────────────────────┤
|
|
220
|
-
│ Entity Indexer + Alias │ Intent Classifier │
|
|
221
|
-
│ Resolver │ (factual/temporal/causal) │
|
|
222
|
-
├─────────────────────────────────────┼────────────────────────────────┤
|
|
223
|
-
│ Knowledge Graph (SQLite) │ LanceDB (Vectors) │
|
|
224
|
-
│ Entity nodes + edges │ 768-dim fastembed (ONNX) │
|
|
225
|
-
│ Causal triple inference │ Zettelkasten notes │
|
|
226
|
-
│ SQLite WAL (TypeDB via extension) │ IVF_PQ index │
|
|
227
|
-
└─────────────────────────────────────┴────────────────────────────────┘
|
|
228
|
-
```
|
|
229
225
|
|
|
230
226
|
## Extensions
|
|
231
227
|
|
|
@@ -272,6 +268,12 @@ MIT -- See [LICENSE](LICENSE).
|
|
|
272
268
|
|
|
273
269
|
**Made by Patrick Roland**.
|
|
274
270
|
|
|
271
|
+
## Support the Project
|
|
272
|
+
|
|
273
|
+
ZettelForge is MIT-licensed. If it's useful in your workflow and you'd like to help keep it maintained:
|
|
274
|
+
|
|
275
|
+
<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>
|
|
276
|
+
|
|
275
277
|
## Acknowledgments
|
|
276
278
|
|
|
277
279
|
- Inspired by [Zettelkasten](https://en.wikipedia.org/wiki/Zettelkasten) and [A-Mem](https://arxiv.org/abs/2602.10715) (NeurIPS 2025)
|
|
@@ -279,3 +281,5 @@ MIT -- See [LICENSE](LICENSE).
|
|
|
279
281
|
- STIX 2.1 schema informed by [typedb-cti](https://github.com/typedb-osi/typedb-cti)
|
|
280
282
|
- Benchmarked against [LOCOMO](https://snap-research.github.io/locomo/) (ACL 2024) and [CTIBench](https://arxiv.org/abs/2406.07599) (NeurIPS 2024)
|
|
281
283
|
- [LanceDB](https://lancedb.com) | [fastembed](https://github.com/qdrant/fastembed) | [Pydantic](https://pydantic.dev) | [TypeDB](https://typedb.com)
|
|
284
|
+
|
|
285
|
+
[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/
|
|
@@ -2,28 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
**The only agentic memory system built for cyber threat intelligence.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Persistent memory for AI agents and Claude Code — with CTI entity extraction, STIX knowledge graphs, threat-actor alias resolution, and offline-first RAG. MCP server included. No cloud, no API keys.
|
|
6
6
|
|
|
7
7
|
[](https://pypi.org/project/zettelforge/)
|
|
8
|
-
[](https://pepy.tech/projects/zettelforge)
|
|
9
|
-
[](https://github.com/rolandpg/zettelforge/stargazers)
|
|
10
|
-
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](https://pepy.tech/projects/zettelforge)
|
|
11
9
|
[](https://www.python.org/downloads/)
|
|
10
|
+
[](https://opensource.org/licenses/MIT)
|
|
12
11
|
[](https://github.com/rolandpg/zettelforge/actions)
|
|
13
|
-
[](https://github.com/rolandpg/zettelforge/graphs/contributors)
|
|
14
|
-
[](https://github.com/rolandpg/zettelforge/commits/master)
|
|
15
|
-
[](https://safeskill.dev/scan/rolandpg-zettelforge)
|
|
16
12
|
|
|
13
|
+
**[⭐ Star](https://github.com/rolandpg/zettelforge) · [📦 `pip install zettelforge`](https://pypi.org/project/zettelforge/) · [📖 Docs](https://docs.threatrecall.ai/) · [🧪 Hosted](https://threatrecall.ai)**
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
<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>
|
|
17
|
+
</p>
|
|
17
18
|
<p align="center">
|
|
18
|
-
<img src="docs/assets/demo.gif" width="720" alt="ZettelForge demo — CTI agentic memory in action">
|
|
19
|
+
<img src="https://raw.githubusercontent.com/rolandpg/zettelforge/master/docs/assets/demo.gif" width="720" alt="ZettelForge demo — CTI agentic memory in action">
|
|
19
20
|
</p>
|
|
20
21
|
|
|
22
|
+
> If ZettelForge fits a CTI workflow you run, a star is the fastest signal that this category is worth continuing to invest in.
|
|
23
|
+
|
|
21
24
|
## Why ZettelForge?
|
|
22
25
|
|
|
23
26
|
General-purpose memory systems don't understand threat intelligence. They can't tell APT28 from Fancy Bear, don't know that CVE-2024-3094 is the XZ Utils backdoor, and can't track how intelligence evolves across reports. When your agent forgets context between investigations, you end up re-reading the same reports and re-building the same mental models.
|
|
24
27
|
|
|
25
28
|
ZettelForge was built from the ground up for analysts who think in threat graphs, not chat histories. It extracts CVEs, threat actors, IOCs, and MITRE 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 offline, with no API keys or cloud dependencies.
|
|
26
29
|
|
|
30
|
+
>"Memory augmentation closes 33% of the gap between small and large models on CTI tasks (CTI-REALM, Microsoft 2026)." [1]
|
|
31
|
+
|
|
32
|
+
|
|
27
33
|
| Feature | ZettelForge | Mem0 | Graphiti | Cognee |
|
|
28
34
|
|---------|------------|------|----------|--------|
|
|
29
35
|
| CTI entity extraction (CVEs, actors, IOCs) | Yes | No | No | No |
|
|
@@ -35,6 +41,12 @@ ZettelForge was built from the ground up for analysts who think in threat graphs
|
|
|
35
41
|
| OCSF audit logging | Yes | No | No | No |
|
|
36
42
|
| MCP server (Claude Code) | Yes | No | No | No |
|
|
37
43
|
|
|
44
|
+
## Data Pipeline
|
|
45
|
+
<p align="center">
|
|
46
|
+
<img src="https://raw.githubusercontent.com/rolandpg/zettelforge/master/docs/assets/zettelforge_architecture.svg" width="720" alt="ZettelForge architecture — Extract, Graph, Embed, Recall pipeline">
|
|
47
|
+
</p>
|
|
48
|
+
|
|
49
|
+
|
|
38
50
|
## Features
|
|
39
51
|
|
|
40
52
|
**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.
|
|
@@ -100,7 +112,7 @@ mm.remember(
|
|
|
100
112
|
|
|
101
113
|
Every `remember()` call triggers a pipeline:
|
|
102
114
|
|
|
103
|
-
1. **Entity Extraction** -- regex + LLM NER identifies CVEs, actors, tools, campaigns, people, locations,
|
|
115
|
+
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)
|
|
104
116
|
2. **Knowledge Graph Update** -- entities become nodes, co-occurrence becomes edges, LLM infers causal triples
|
|
105
117
|
3. **Vector Embedding** -- 768-dim fastembed (ONNX, in-process, 7ms/embed) stored in LanceDB
|
|
106
118
|
4. **Supersession Check** -- entity overlap detection marks stale notes as superseded
|
|
@@ -158,26 +170,6 @@ python examples/athf_bridge.py /path/to/hunts/
|
|
|
158
170
|
|
|
159
171
|
See [examples/athf_bridge.py](examples/athf_bridge.py).
|
|
160
172
|
|
|
161
|
-
## Architecture
|
|
162
|
-
|
|
163
|
-
```
|
|
164
|
-
┌──────────────────────────────────────────────────────────────────────┐
|
|
165
|
-
│ MemoryManager │
|
|
166
|
-
│ remember() remember_with_extraction() recall() synthesize() │
|
|
167
|
-
├──────────┬───────────┬──────────────┬───────────┬────────────────────┤
|
|
168
|
-
│ Note │ Fact │ Memory │ Blended │ Synthesis │
|
|
169
|
-
│Constructor│ Extractor │ Updater │ Retriever │ Generator │
|
|
170
|
-
│(enrich) │(Phase 1) │(Phase 2) │(vec+graph)│ (RAG) │
|
|
171
|
-
├──────────┴───────────┴──────────────┼───────────┴────────────────────┤
|
|
172
|
-
│ Entity Indexer + Alias │ Intent Classifier │
|
|
173
|
-
│ Resolver │ (factual/temporal/causal) │
|
|
174
|
-
├─────────────────────────────────────┼────────────────────────────────┤
|
|
175
|
-
│ Knowledge Graph (SQLite) │ LanceDB (Vectors) │
|
|
176
|
-
│ Entity nodes + edges │ 768-dim fastembed (ONNX) │
|
|
177
|
-
│ Causal triple inference │ Zettelkasten notes │
|
|
178
|
-
│ SQLite WAL (TypeDB via extension) │ IVF_PQ index │
|
|
179
|
-
└─────────────────────────────────────┴────────────────────────────────┘
|
|
180
|
-
```
|
|
181
173
|
|
|
182
174
|
## Extensions
|
|
183
175
|
|
|
@@ -224,6 +216,12 @@ MIT -- See [LICENSE](LICENSE).
|
|
|
224
216
|
|
|
225
217
|
**Made by Patrick Roland**.
|
|
226
218
|
|
|
219
|
+
## Support the Project
|
|
220
|
+
|
|
221
|
+
ZettelForge is MIT-licensed. If it's useful in your workflow and you'd like to help keep it maintained:
|
|
222
|
+
|
|
223
|
+
<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>
|
|
224
|
+
|
|
227
225
|
## Acknowledgments
|
|
228
226
|
|
|
229
227
|
- Inspired by [Zettelkasten](https://en.wikipedia.org/wiki/Zettelkasten) and [A-Mem](https://arxiv.org/abs/2602.10715) (NeurIPS 2025)
|
|
@@ -231,3 +229,5 @@ MIT -- See [LICENSE](LICENSE).
|
|
|
231
229
|
- STIX 2.1 schema informed by [typedb-cti](https://github.com/typedb-osi/typedb-cti)
|
|
232
230
|
- Benchmarked against [LOCOMO](https://snap-research.github.io/locomo/) (ACL 2024) and [CTIBench](https://arxiv.org/abs/2406.07599) (NeurIPS 2024)
|
|
233
231
|
- [LanceDB](https://lancedb.com) | [fastembed](https://github.com/qdrant/fastembed) | [Pydantic](https://pydantic.dev) | [TypeDB](https://typedb.com)
|
|
232
|
+
|
|
233
|
+
[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/
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
Report security issues by email to:
|
|
8
8
|
|
|
9
|
-
**
|
|
9
|
+
**contact@threatrecall.ai**
|
|
10
10
|
|
|
11
11
|
Include in your report:
|
|
12
12
|
- A description of the vulnerability and its potential impact
|
|
@@ -39,9 +39,9 @@ backported to the prior minor release.
|
|
|
39
39
|
|
|
40
40
|
| Version | Supported |
|
|
41
41
|
|---|---|
|
|
42
|
-
| 2.
|
|
43
|
-
| 2.
|
|
44
|
-
| < 2.
|
|
42
|
+
| 2.3.x (current) | Yes — active security support |
|
|
43
|
+
| 2.2.x | Critical fixes only, for 60 days after 2.3.0 release |
|
|
44
|
+
| < 2.2 | No — upgrade required |
|
|
45
45
|
|
|
46
46
|
---
|
|
47
47
|
|
|
@@ -53,8 +53,9 @@ The following components are covered by this policy:
|
|
|
53
53
|
|
|
54
54
|
- **Memory pipeline** — `remember()`, `recall()`, `synthesize()`, and
|
|
55
55
|
the two-phase extraction pipeline
|
|
56
|
-
- **Storage layer** —
|
|
57
|
-
index
|
|
56
|
+
- **Storage layer** — SQLite backend (notes, knowledge graph, entity index)
|
|
57
|
+
and the LanceDB vector index. Legacy JSONL paths still present for
|
|
58
|
+
migration are also in scope.
|
|
58
59
|
- **MCP server** — all tool handlers exposed to Claude Code and other
|
|
59
60
|
MCP clients
|
|
60
61
|
- **REST API** — all FastAPI endpoints in `src/zettelforge/server.py`
|
|
@@ -83,11 +84,16 @@ The following components are covered by this policy:
|
|
|
83
84
|
|
|
84
85
|
### Data at Rest
|
|
85
86
|
|
|
86
|
-
- Notes
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
87
|
+
- Notes, the knowledge graph, and the entity index are stored in a local
|
|
88
|
+
SQLite database (WAL mode) under the configured data directory. No
|
|
89
|
+
encryption at rest is applied by ZettelForge itself — encrypt the
|
|
90
|
+
filesystem or volume at the OS level for sensitive deployments.
|
|
91
|
+
- LanceDB vector index files live alongside the SQLite database and
|
|
92
|
+
carry the same recommendation.
|
|
93
|
+
- Legacy v2.1.x deployments that still use JSONL (`notes.jsonl`,
|
|
94
|
+
`kg_nodes.jsonl`, `kg_edges.jsonl`, `entity_index.json`) should run
|
|
95
|
+
`scripts/migrate_jsonl_to_sqlite.py` — the JSONL paths are no longer
|
|
96
|
+
the default but remain supported as a migration input.
|
|
91
97
|
|
|
92
98
|
### Injection Defenses
|
|
93
99
|
|