Skip to content

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.