Configure OpenCTI integration¶
In progress
Automated bi-directional OpenCTI sync is not yet available in ZettelForge OSS or ThreatRecall.ai. This page documents the configuration keys that exist today, the manual STIX exchange path that works now, and the relationship ingestion API for programmatic graph writes. Full connector support is on the roadmap.
OpenCTI is an open-source threat intelligence platform that stores indicators, reports, and relationships in a structured graph. ZettelForge is a complementary memory layer for CTI analysts: it enriches raw intelligence with entity extraction, alias resolution, and semantic recall. This guide shows you how to connect them today using STIX as the data exchange format, and what to expect when automated sync ships.
Prerequisites¶
- ZettelForge 2.7.0 or later installed (see Quickstart)
- A running OpenCTI instance reachable from your environment
- For ThreatRecall.ai STIX exchange: an active account and API token
What works today¶
Manual STIX exchange via ThreatRecall.ai¶
The most reliable path today is a STIX roundtrip through the ThreatRecall.ai API. You export from OpenCTI as a STIX 2.1 bundle, import it into ThreatRecall.ai, and export recall sessions back to OpenCTI as STIX.
Import a STIX bundle from OpenCTI:
# Export from OpenCTI (use your OpenCTI UI or API)
# Then import the resulting bundle into ThreatRecall.ai:
curl -X POST https://app.threatrecall.ai/api/ingest/stix \
-H "Authorization: Bearer $THREATRECALL_TOKEN" \
-H "Content-Type: application/json" \
-d @opencti-export.stix.json
Export a recall session back to OpenCTI:
# Run a recall session first, capture the session ID from the response,
# then export it as STIX:
curl -sS https://app.threatrecall.ai/api/recall/sessions/$RECALL_SESSION_ID/export.stix \
-H "Authorization: Bearer $THREATRECALL_TOKEN" \
-o recall-session.stix.json
# Import the file into OpenCTI using its STIX import connector or the API.
Both endpoints are implemented and validated (see AGE-138 connector validation, 2026-06-15).
Programmatic relationship ingestion (ZettelForge OSS)¶
If you write a custom connector or sync script, you can push OpenCTI relationships
directly into the ZettelForge knowledge graph using ingest_relationship(). This
method is available in the OSS package and does not require any extension.
from zettelforge import MemoryManager
mm = MemoryManager()
# Push a relationship from your OpenCTI data
mm.ingest_relationship(
from_type="actor",
from_value="APT28",
to_type="tool",
to_value="Mimikatz",
relationship="USES_TOOL",
properties={"confidence": 0.85, "source": "opencti-export"},
)
The method is idempotent: calling it again with the same entity-relationship triple updates edge properties without creating a duplicate edge.
Method signature:
| Parameter | Type | Description |
|---|---|---|
from_type |
str |
Source entity type ("actor", "malware", "campaign") |
from_value |
str |
Canonical source entity value |
to_type |
str |
Target entity type |
to_value |
str |
Canonical target entity value |
relationship |
str |
Relationship label (e.g. "USES_TOOL", "TARGETS_ASSET") |
properties |
dict or None |
Optional edge properties: confidence, timestamps, etc. |
Configuration keys (ZettelForge OSS)¶
ZettelForge 2.7.0 includes OpenCTI config keys that parse correctly from
config.yaml. Automated sync against a live OpenCTI instance is not yet
implemented in the OSS package, but you can set these for future compatibility
when the sync connector ships.
Add to your config.yaml:
opencti:
url: http://localhost:8080 # OpenCTI instance URL
token: "" # OpenCTI API token
sync_interval: 0 # Seconds between sync cycles; 0 = disabled (default)
sync_interval: 0 is the default and disables any sync polling. Setting a value
here has no effect in the current OSS release because the sync worker is not yet
implemented.
Roadmap¶
Automated bi-directional sync between OpenCTI and ZettelForge is planned. When it ships, this page will be updated with working connector configuration. Until then:
- Use STIX import/export (ThreatRecall.ai) for structured data exchange.
- Use
ingest_relationship()to push relationship data programmatically from your own connector script.
Related¶
- Quickstart: get ZettelForge running first
- First CTI report: ingest and recall threat data
- Two products, one core: ZettelForge OSS vs ThreatRecall.ai SaaS