Skip to content

Use the web management interface

ZettelForge ships a browser-based management interface served at http://localhost:8088. It covers system health monitoring, threat intelligence search and synthesis, knowledge graph exploration, log streaming, bulk data ingestion, entity browsing, session history, and live configuration editing.

Prerequisites

  • ZettelForge installed: pip install zettelforge
  • Web dependencies installed: pip install zettelforge[web]
  • At least one note stored (see Quickstart)

Step 1: Start the server

python web/app.py

Expected output:

ZettelForge v2.7.0 — http://127.0.0.1:8088

Open http://localhost:8088 in your browser.

Custom port

python web/app.py --port 9000

Disable the web UI

Set ZETTELFORGE_WEB_ENABLED=false in your environment, or add the following to your config.yaml:

web:
  enabled: false

Step 2: Navigate the interface

The interface has three zones: a header (top bar with brand mark and operational stats), a sidebar (navigation links), and a content area.

Click any sidebar item to navigate. The active item has a neon green left border.

Sidebar item Icon Description
Dashboard layout-dashboard System health tiles, telemetry summary, quick actions
Search search Threat intelligence recall, synthesis, and storage
Knowledge Graph git-graph 3D orbital entity graph with search and filters
Logs & Telemetry scroll-text Log file tailing, telemetry charts, live streaming
Ingest upload Manual and bulk data ingestion
Entities database Paginated entity browser with type/tier/text filters
History clock Recent session activity with date filters
Configuration settings Feature flag toggles and raw YAML editor

Step 3: Explore each view

Dashboard

The dashboard shows system health at a glance. Four status tiles display:

  • Storage — backend type (sqlite), total notes, total entities, data directory size
  • LLM — provider (ollama/local/litellm), model name, local backend engine, uptime
  • Embedding — provider (fastembed/ollama), model name, vector dimensions
  • Queue — enrichment queue depth and operational status

Below the tiles is a telemetry summary showing today's query volume, synthesis count, and average latency. A bar chart breaks down query intents (factual, temporal, relational, causal, exploratory). Quick action buttons let you run LanceDB compaction or force a config reload.

Three tabs:

  • Recall — Enter a query (for example, "What tools does APT28 use?"). Results appear as cards showing note ID, tier pill (A/B/C color-coded), domain, content, entity tags, confidence, and timestamp.
  • Synthesize — Same query input with a format selector (direct_answer, synthesized_brief, timeline_analysis, relationship_map). Note: synthesized_brief, timeline_analysis, and relationship_map are gated to the ThreatRecall.ai SaaS; the OSS build falls back to direct_answer automatically. See Memory Manager API.
  • Remember — Textarea for storing new CTI content. After submission, shows note ID, status (ADD/UPDATE/DELETE/NOOP), latency, and extracted entities.

Knowledge Graph

The knowledge graph view renders entity nodes and relationship edges as a 3D orbital canvas. Drag to rotate, scroll to zoom, click any node for a detail panel.

Node colors are assigned by entity type:

Entity type Color Hex
cve red #F85149
tool purple #A371F7
campaign amber #D29922
other grey #8B949E

Note

The color map in the current frontend uses the key actor, but ZettelForge stores threat actor entities with type intrusion_set. As a result, intrusion_set nodes render as grey rather than blue. This is a known frontend issue.

Edge colors are assigned by relationship type: uses (purple), targets (red), related_to (blue), attributed_to (amber). All other edge types use the default border color.

Click any node to open a detail panel showing entity name, type, tier, confidence, and connected entities. Use the search input to filter nodes by label. The filter panel lets you toggle labels or filter by entity type.

Logs & Telemetry

Two-panel layout:

  • Log panel — Filterable table of structlog events. Columns: timestamp, level badge (ERROR=red, WARNING=amber, INFO=default, DEBUG=dim), logger name, message. Enable auto-refresh to poll every 3 seconds. Click any row to expand the full structured JSON context.
  • Telemetry panel — Aggregated daily statistics: query volume, latency percentiles, intent distribution.

Ingest

Three input modes:

  • Text area — Paste CTI content and submit. Configure source metadata (domain: cti/sigma/yara/report/general; source_type: manual/report/feed/api; evolve toggle). After submission, shows the note ID, extracted entities, and latency.
  • Bulk mode — Multi-line textarea, one item per line. Progress shows N/M stored with per-item success/failure status.
  • File upload — Drag and drop .txt, .md, .json (STIX bundle), or .csv files. File content populates the textarea for review before submission.

Entities

A paginated table of all entities in the knowledge graph. Columns: entity name, type badge, tier pill, confidence, connected count. Use the filter bar (type dropdown, tier checkboxes, text search) to narrow results. Click column headers to sort. Click any row for an inline detail panel.

History

A chronological table of recent activity. Columns: timestamp, type badge (recall=blue, synthesis=purple, remember=green), query text, latency (ms), result count, intent. Use the date range filter (today, 7 days, 30 days, all). Click any row to expand full details, re-run a query, or export JSON.

Configuration

Two modes:

  • Feature Flags — Grouped cards showing every config section. Boolean fields render as toggle switches. Text/number fields are editable inputs. Secrets show as *** and are read-only. Fields requiring a restart show an amber "Restart Required" badge.
  • YAML Editor — Full config.yaml content in a monospace textarea. Edit directly, then click "Apply".

API authentication

All API endpoints (except GET /) require authentication. Two modes:

Loopback (default). When a request comes from a loopback address (127.0.0.1, ::1, or localhost) and no API key is configured, the request is allowed without a key. Running python web/app.py binds to 127.0.0.1 by default, so local browser access requires no key.

API key. When ZETTELFORGE_WEB_API_KEY is set in your environment, all requests must supply that key. Pass it with either the X-API-Key header or as a Bearer token in the Authorization header:

export ZETTELFORGE_WEB_API_KEY=my-secret-key
curl -H "X-API-Key: my-secret-key" http://your-host:8088/api/health

Binding to all interfaces (--host 0.0.0.0) requires ZETTELFORGE_WEB_API_KEY to be set; the server will refuse to start without it:

ZETTELFORGE_WEB_API_KEY=my-secret-key python web/app.py --host 0.0.0.0

When you open the interface from a non-loopback host, the SPA prompts you to enter the API key. It stores the key in the browser's localStorage under the key zettelforgeApiKey. You can also set it directly:

localStorage.setItem('zettelforgeApiKey', 'my-secret-key');

Single-tenant mode

The OSS web interface runs in single-tenant mode — no user accounts or session management. Multi-tenant OAuth/JWT authentication is available via ThreatRecall.ai SaaS.


Next steps