Skip to main content

Klaxon

The alarm tells you that. Klaxon tells you what.

Klaxon is a read-only proxy in front of your Wazuh 5 indexer. A language model asks questions in plain language; Klaxon queries the indexer, reads the schema, tests decoders, and reports what it actually found — including when it found nothing, and why. Works with Claude Desktop, Claude Code, local models through Ollama, and Open WebUI.

Every query runs through Klaxon, and the response is masked before it reaches an external LLM: a value under a configured field (user.name, source.ip, …) always becomes the same deterministic token ([USER_…], [IP_…]), aggregation keys included. This is pseudonymization, not anonymization — tokens are deterministic and reversible by anyone holding the salt (see LLM-safety guarantees).


Quick start

Requirements

  • Wazuh 5.x indexer reachable over HTTPS (search/schema also work against 4.x)
  • Python 3.11+ or Docker
  • an MCP client — Claude Desktop, Claude Code, ollmcp, Open WebUI 0.6.31+

1. Install

python3 -m venv .venv
.venv/bin/pip install klaxon-mcp

Or build the Docker image (klaxon-mcp is the entry point):

docker build -t klaxon-mcp .

2. Point it at your indexer

export KLAXON_INDEXER_URL=https://indexer.example:9200
export KLAXON_INDEXER_USER=wazuh-readonly
export KLAXON_INDEXER_PASSWORD=...

KLAXON_INDEXER_URL is the only required variable. Add KLAXON_MANAGER_URL for manager/detectors and KLAXON_ENGINE_URL for tester_sessions; set KLAXON_VERIFY_SSL=false only for a self-signed lab cluster.

3. Enable masking (recommended for external LLMs)

Masking is off by default and opt-in:

export KLAXON_ANONYMIZE_EXTERNAL_LLM=true   # mask tool output for external models
export KLAXON_ANONYMIZATION_SALT=change-me-to-a-long-random-secret  # stable tokens

With the switch on, output is masked unless KLAXON_LLM_BASE_URL points at a loopback address (http://localhost:11434 for Ollama) — a local model keeps receiving unchanged data. An optional config.yaml (KLAXON_CONFIG) holds only what you change; environment variables always win:

anonymization:
  mask_fields:                 # or KLAXON_ANONYMIZATION_MASK_FIELDS
    - "source.ip"
    - "user.name"
    - "host.hostname"
  mask_aggregation_keys: true  # ON by default; false disables agg-key masking

4. Start it

klaxon-mcp    # stdio — your MCP client spawns it (klaxon is an alias)

With Docker: docker run --rm -i --env-file .env klaxon-mcp.

5. First masked result

Ask your client: "Show me the last login by user.name=alice in wazuh-events-v5-*." Klaxon runs search(index="wazuh-events-v5-*", body=…) and returns the masked response:

{
  "hits": { "hits": [ { "_source": {
      "user":    { "name": "[USER_9f2a1c467dd5e2b8]" },
      "source":  { "ip": "[IP_5c01e73f9a2b4c1d]" },
      "message": "user [USER_9f2a1c467dd5e2b8] logged in via ssh from [IP_5c01e73f9a2b4c1d]"
  } } ] }
}

The same value always maps to the same token. Masking is pseudonymization, not anonymization, and it has documented blind spots — see docs/llm-safety.md (in particular the verified leaks in "Known limitations") before pointing an external model at Klaxon.


Basic usage

The handful of tools a normal user needs (full reference: docs/TOOLS.md):

Tool What it does One-liner example
search Any query against any index, raw JSON back search(index="wazuh-events-v5-*", body={"query": {"match_all": {}}})
schema Which fields exist — and which actually carry data schema(index="wazuh-events-v5-*", prefix="wazuh.agent.")
field_coverage How complete each field is, window vs all history field_coverage(index="wazuh-events-v5-*", prefix="event.")
findings_overview Findings by severity, agent, title, category findings_overview(hours=48)
logtest Push a raw line through the decoder chain logtest(event="<raw line>")
gdpr_check Find sensitive fields the mask list should cover gdpr_check(index="wazuh-events-v5-*")
klaxon_posture_check Read-only security posture: facts + gaps, no verdict klaxon_posture_check(tenant="customer-a")

On an unfamiliar cluster, start with field_coverage. Every thin result gets a notice block before the data (empty aggregation, capped size, missing index — all return HTTP 200 with nothing).


Configuration (essentials)

The keys a normal user changes day to day. Full reference: docs/configuration.md.

Variable / key What it does Default
KLAXON_INDEXER_URL Indexer endpoint — (required)
KLAXON_INDEXER_USER / KLAXON_INDEXER_PASSWORD Basic-auth credentials empty
KLAXON_ANONYMIZE_EXTERNAL_LLM Master masking switch false
KLAXON_ANONYMIZATION_SALT Secret for token derivation (stable tokens) random+persisted
KLAXON_ANONYMIZATION_MASK_FIELDS Fields masked wholesale (user.name, source.ip, …) built-in list
KLAXON_ANONYMIZATION_MASK_AGGREGATION_KEYS Mask aggregation bucket keys too true (fail-closed)
KLAXON_ANONYMIZATION_MASK_FREE_TEXT_USERS Mask usernames inside free text true
KLAXON_VERIFY_SSL TLS verification true
KLAXON_MCP_AUTH_TOKEN Required bearer token when serving over HTTP empty

Advanced topics

The deep material lives in dedicated docs — linked, not duplicated:

  • GDPR plausibility checker (classification layers, custom rules, sampling, reports) → docs/gdpr-checker.md
  • Ingest masking / Option B masked stream (pipeline, ISM, index templates, quarantine for masking failures, sync job) → docs/option-b-masked-stream.md
  • Multi-tenant setup (fields.yaml, klaxon masking generate, salt, namespacing) → docs/multi-tenant.md
  • Drift prevention & CI (pre-commit drift hook, provenance fingerprints, fail-closed startup, sync preflight, --verify-config) → docs/drift-prevention.md
  • Token scheme & security model (HMAC, salt, self-test, why 16 hex) → docs/security-model.md
  • Security concept: brute-force re-identification risk (pseudonymization vs anonymization, salt as secret) → docs/security-concept.md
  • Salt rotation runbook (no scheduled rotation; only on suspicion; response-layer + masked-stream paths) → docs/salt-rotation-runbook.md
  • LLM safety: using Klaxon safely with an LLM (masked-stream-first routing, ready-to-copy system prompt, pseudonymization boundary, residual gate) → docs/llm-safety.md
  • Running it on another machine (HTTP transport, auth, TLS, CORS) → docs/TOOLS.md, ARCHITECTURE.md

Development

.venv/bin/pip install -e ".[dev]"
.venv/bin/pytest        # full suite
.venv/bin/mypy          # strict type check
.venv/bin/ruff check src

Option B generator self-tests (see docs/drift-prevention.md):

klaxon masking selftest --tenant customer-a
klaxon masking generate --check   # CI/pre-commit drift check

Deploy the masking artifacts to the indexer in one idempotent, ordered, self-verifying step (preflight + GET-back verification + a _simulate smoke test; --dry-run / --rollback):

klaxon masking deploy --tenant customer-a --dry-run   # plan only, no writes
klaxon masking deploy --tenant customer-a             # needs KLAXON_INDEXER_*

Remove the Option B masking infrastructure from the indexer cleanly, leaving the raw Wazuh streams untouched (destructive — preview with --dry-run; a mandatory verification phase proves nothing klaxon-* is left and the raw streams are intact):

klaxon masking teardown --tenant customer-a --dry-run       # plan only, no writes
klaxon masking teardown --tenant customer-a --yes           # needs KLAXON_INDEXER_*
klaxon masking teardown --tenant customer-a --yes --purge-sync-state
#   ^ also delete the sync checkpoint marker (default: keep it so a future
#     re-setup can resume from the last checkpoint)

The live integration test (klaxon masking test) needs real indexer credentials — see docs/option-b-masked-stream.md. Release history: CHANGELOG.md.


Documentation


License

Apache-2.0 — see LICENSE.

Built by sec73 GmbH.

Wazuh is a registered trademark of Wazuh Inc. Klaxon is an independent project and is not affiliated with, endorsed by, or sponsored by Wazuh Inc.

Release files for klaxon-mcp 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for klaxon-mcp 0.2.1
File Size Uploaded
klaxon_mcp-0.2.1.tar.gz 436.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for klaxon-mcp 0.2.1
File Interpreter ABI Platform
klaxon_mcp-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 659.5 kB

Release files / klaxon_mcp-0.2.1.tar.gz

Download URL klaxon_mcp-0.2.1.tar.gz
Size 436.7 kB
Tags Source
SHA-256 checksum
How to use checksums
c996befde63a5d377fcf87e9f3205d83d17b9d1bb7fcd56590b44e53c6fdeb09
BLAKE2b-256 checksum
How to use checksums
0ae4a7ac00cafe63b96f451df8b8a4a4ff1900f64ab3d8196957cf8c8ca96c23
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.5

Release files / klaxon_mcp-0.2.1-py3-none-any.whl

Download URL klaxon_mcp-0.2.1-py3-none-any.whl
Size 222.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
aef51eb03a0d6e508b164c0bbbe6718fc27e50813b2a6a73ffe1948de6bf578b
BLAKE2b-256 checksum
How to use checksums
283c18d63188f3b260232c401749feadca253b0c3645f0b7ddc15cef5e632312
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.5

Release history Release notifications | RSS feed

This release

0.2.1 This release

2 release files

0.2.0

2 release files

0.1.7

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

0.0.2

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page