STIX alignment in ZettelForge¶
ZettelForge 2.8.0 is a CTI memory system, not a STIX 2.1 object store. It extracts STIX-adjacent labels from unstructured text and stores them as note metadata and graph entities. ThreatRecall.ai is the separate hosted SaaS and owns native STIX bundle interchange.
This boundary lets you capture intelligence quickly without mistaking a ZettelForge note for a standards-conformant STIX object.
Prerequisites: None. Use the STIX schema reference for exact labels and fields, and the KG edge schema for stored relationship shapes.
The mental model¶
The OASIS STIX 2.1 specification separates threat intelligence into domain objects (SDOs), cyber observable objects (SCOs), and relationship objects (SROs). ZettelForge borrows that vocabulary at three lighter-weight layers:
- A memory note keeps the source text and editorial metadata.
- The extractor attaches typed strings such as
intrusion_set,cve,ipv4, andattack_pattern. - The graph connects those strings with labels such as
USES_TOOL,EXPLOITS_CVE, andMENTIONED_IN.
The labels preserve enough structure for CTI retrieval. They do not turn the note, entity, or edge into a STIX object.
Where the vocabulary aligns¶
The released extractor recognizes 19 labels:
| Layer | ZettelForge labels | STIX relationship |
|---|---|---|
| CTI subjects | cve, intrusion_set, actor, tool, campaign, attack_pattern |
SDO-adjacent subjects |
| Observables | ipv4, domain, url, md5, sha1, sha256, email |
SCO-adjacent values |
| Conversational context | person, location, organization, event, activity, temporal |
ZettelForge context, not STIX objects |
The distinction between intrusion_set and actor is operational rather than
fully ontological. The deterministic extractor assigns APT, UNC, TA, FIN, and
TEMP designations to intrusion_set; it assigns a short built-in list of named
groups to actor. recall_actor() searches actor, the compatibility label
threat_actor, and intrusion_set so older and newer notes remain findable.
This is alignment, not conformance. A conformant STIX object also carries an object type, a STIX identifier, a specification version, and the common fields required for that object. ZettelForge's extracted strings carry none of that object envelope.
Metadata preserves analyst judgments¶
Each MemoryNote has fields that let an integration retain STIX-adjacent
judgments without adopting the STIX data model:
| Field | Released default | Purpose |
|---|---|---|
metadata.stix_confidence |
-1 |
Holds an integer confidence supplied by your integration; -1 means unset. |
metadata.tier |
"B" |
Records ZettelForge's editorial tier: authoritative, operational, or support. |
metadata.tlp |
"" |
Stores the marking text supplied by a caller. |
metadata.vuln |
None |
Holds optional CVSS, EPSS, and CISA KEV fields for vulnerability notes. |
The released Pydantic models store these values but do not enforce their documented numeric ranges. Your importer remains responsible for validating a STIX confidence value before assigning it.
TLP enforcement is in progress
ZettelForge 2.8.0 stores metadata.tlp as an unrestricted string. The
released package does not normalize TLP values or apply a TLP ceiling in
core recall and snapshot paths. Enforce sharing policy in the calling
application. ThreatRecall.ai applies its own tenant-scoped controls in the
hosted service.
The separation between stix_confidence and tier is deliberate. A numeric
score describes confidence in one assertion. A tier describes the editorial
role of the source. Keeping both prevents an authoritative advisory and an
unverified report from becoming indistinguishable just because they carry the
same number.
Relationships stay flexible¶
ZettelForge writes graph nodes and edges to SQLite and mirrors explicit
ingest_relationship() writes to its legacy JSONL graph. The SQLite edge key
uses the source node, target node, relationship label, and optional note_id
provenance. Repeating the same key updates its properties.
MemoryManager does not validate relationship labels against a STIX schema.
That flexibility is useful when raw reports describe organization-specific
relationships, but it also means an edge named USES_TOOL is not a STIX
Relationship Object. The separate ontology validator exposes a stricter
vocabulary, yet the v2.8.0 release does not wire it into MemoryManager writes.
Why ZettelForge stops short of native STIX¶
ZettelForge accepts conversational notes, reports, and observations as raw text. Its deterministic extractor can identify a CVE, ATT&CK technique, threat group, tool, or observable without first constructing a complete bundle. That keeps the ingestion path useful for material that never arrives as STIX JSON.
A native STIX store solves a different problem: preserving object identifiers, required properties, markings, relationship endpoints, and bundle round trips. ZettelForge 2.8.0 does not parse or emit STIX bundles, generate STIX object IDs, or validate SDOs, SCOs, and SROs.
The release also contains legacy hooks that look for an external TypeDB adapter, but the open-source distribution does not ship that adapter or its driver. The default graph path remains SQLite with a JSONL compatibility mirror.
TypeDB integration is in progress
Treat the TypeDB loader hooks as compatibility code, not as a shipped ZettelForge backend. The released adapter tests skip when the absent adapter cannot be imported.
The two-product boundary¶
Use ZettelForge when you want self-hosted memory and retrieval over raw CTI text. Build custom integration code when you need to translate its notes and edges into another platform's STIX model.
Use ThreatRecall.ai when you need hosted STIX interchange. Its current OpenAPI
surface exposes authenticated STIX import at /api/ingest/stix and STIX export
from recall sessions and threat-agent graph workflows. The service maps those
objects to tenant-scoped PostgreSQL kg_nodes and kg_edges records.
That product boundary is the central design choice: ZettelForge optimizes the path from unstructured evidence to recallable memory; ThreatRecall.ai owns the standards-conformant interchange surface.