Skip to main content

Nyx Agentic Detection & Response (ADR)

Part of the Nyx Security platform (root README) — the ADR capability: the in-process instrument SDK (nyx/) + the control plane / agentic SIEM (control-plane/). Its sibling is Agentic Security Posture at products/posture/. Integration flows one-way, posture → ADR.

An agent-embedded security monitor. Nyx instruments the boundaries of an agent process in-place and reports findings to a control plane. It runs inside the agent — no proxy, no sidecar required — and is designed to do no harm: a bug in instrumentation must never take down the host agent.

The four boundaries

Boundary What it wraps What you learn
model_client openai chat.completions, anthropic messages, litellm, bedrock (boto3 Converse/InvokeModel), vertex (Gemini) Intent: system prompt, messages, tool schemas offered, tool_calls chosen
tool_dispatch LangChain BaseTool.run/arun, LlamaIndex FunctionTool.call, CrewAI BaseTool.run, AutoGen FunctionTool.run Action: this agent, this run, this tool, these args
mcp client ClientSession.call_tool + server FastMCP.call_tool / low-level Server handlers + protocol metadata (transport/server) Action + protocol: tool, args, both sides of the call, and the assessment point for authz / confused-deputy
effect httpx, requests, aiohttp, subprocess.Popen, os.system Ground truth: what actually happened, even from raw code that bypassed every framework

The effect boundary is the backstop: without it, coverage is only as good as the framework-adapter list. With it, Nyx catches the agent writing and running raw code that bypasses every abstraction.

Status

Built and tested, default observe-only:

  • ✅ L1 capture — model_client (OpenAI + Anthropic + litellm + Bedrock + Vertex), tool_dispatch (LangChain + LlamaIndex + CrewAI + AutoGen), mcp (client + server: FastMCP & low-level Server, + protocol metadata), effect (httpx/requests/aiohttp/subprocess/os.system)
  • ✅ L2 attribution — stack/line capture (gated) and identity/NHI capture (cred type, source, fingerprint — never the secret)
  • ✅ L3 correlation — contextvars span chain: effects nest under the tool span that triggered them; whole chain shares a trace_id
  • ✅ L4/L5 policy — decision evaluated on every call and recorded; shadow-mode rollup; enforcement gated behind mode="enforce"
  • ✅ Metadata-only by default (principle 6) — payloads (messages/tool args/response text) are hashed at ship time; metadata always sent. Detectors still see raw content in-process (principle 3: capture in-process, decide locally, ship metadata).
  • ✅ Coverage honesty (principle 7) — subprocess / os.system events carry an explicit coverage_gap (execution leaves the interpreter)
  • ✅ Framework mapping — findings carry version-stamped ATLAS / OWASP Agentic ids
  • ✅ Hardened L1 patching — uses wrapt when installed (descriptor/signature-safe, survives version drift), hardened functools+setattr fallback otherwise; backend-agnostic, idempotent, never crashes import. Guarded by a per-adapter conformance suite.
  • ✅ Self-observability (§9) — nyx.metrics(): events per boundary, coverage gaps, enforced vs. would-act, patch failures, reporter queue health
  • ✅ Co-located engine split (§5/§6.6) — optional UDSSink ships telemetry to a separate python -m nyx.engine process over a Unix socket (fail-open); the blocking decision stays local in-process
  • ✅ Non-blocking reporter (drop-on-overflow), zero hard dependencies (wrapt optional), self-traffic suppression
  • ✅ Overhead measured, not asserted: ~14µs/call wrapper overhead worst-case (trivial callee); ~0.003% of a 500ms model call. Run python benchmarks/overhead.py.

Posture ladder

Nyx walks a trust ladder, set via mode (or NYX_MODE):

The global mode is a master switch over per-rule modes:

Global mode Behavior
observe (default) decisions evaluated + recorded on each event; nothing acted on, nothing shadow-tagged
shadow every rule forced to shadow: would-act events tagged + rolled up, nothing acted on
enforce per-rule honored — rules with mode="enforce" act; rules left in mode="shadow" are tagged would-act

Each rule carries its own mode (shadow/enforce) and fail (open/closed), so you can flip one rule live while the rest stay in shadow. Gate actions: block, redact (mask args before the call), modify, and hold (synchronous approval via a registered approver).

Roadmap

Feature freeze — the umbrella merge is the single active workstream. New features are frozen in both products while ASPM + ADR converge into one repo, one design system, and one security graph. The merged roadmap (merge phases R1–R5 / P1–P5 + both product backlogs) is docs/roadmap.md.

The live, canonical roadmap is the Nyx Roadmap project — that board is the source of truth for the current item list, status, and priorities. ADR phases to date:

  • Phase 1–3 ✅ — single-language spine · correlation & attribution · policy in shadow
  • Phase 4 🟡 — enforcement: per-rule modes, block/redact/modify/hold, engine split, MCP (client + server) / Anthropic / Bedrock / Vertex / LlamaIndex / CrewAI / AutoGen / aiohttp (next: Node/TS stack)
  • Phase 5 ✅ — intelligence: behavioral baselines (per-agent novelty — NYX_BASELINE=1), criticality tagging (NYX_AGENT_CRITICALITY / NYX_AGENT_FUNCTION, findings ranked by blast radius), taint tracking (untrusted ingress → exfil sink, trace-scoped — NYX_TAINT=1), downstream emission schema (nyx.emission/1 → ASPM / AEGIS via POST /api/emit)
  • Phase 6 ✅ — agentic SIEM: smart policy from baselines (nyx.allowlist_rule + control-plane allowlist proposals with anti-poisoning), data-driven correlation/detection rules (lib/correlate.ts, multi-event SIEM over the stream — NYX_CORRELATION_RULES) with a durable alert store (Nyx as system-of-record — Alerts screen, dedup + acknowledge/resolve, survives event aging), Agentic Entity Behaviour Analytics (AEBA) (per-agent fleet risk profile, Entities screen) — built from reviewed plans in docs/plans/

See the project board for the detailed, current item list and status, and docs/plans/ for version-controlled, adversarially-reviewed implementation plans.

Shadow mode

nyx.install(mode="shadow")              # evaluate + tag + aggregate, never block
nyx.policy().add_precall(egress_allowlist)
...
nyx.shadow_summary()                     # {"total_would_act": 3, "by_policy": {...}, "examples": {...}}

Every non-allow decision is tagged onto its event (event["shadow"]) and rolled up; a shadow_summary event is emitted every shadow_summary_interval seconds and on shutdown. See examples/shadow_demo.py.

Quick start

import nyx

nyx.install()  # ConsoleSink: events to stderr
# or: nyx.install(endpoint="https://cp.example/ingest", api_key="...")

with nyx.run(agent_id="research-agent", session_id="sess-42"):
    ...  # run your agent; every event shares a trace_id

Everything is env-overridable for zero-code-change container drop-in: NYX_ENDPOINT, NYX_API_KEY, NYX_ENFORCE, NYX_CAPTURE_CONTENT, NYX_CAPTURE_STACK, NYX_SYNC, NYX_SERVICE, NYX_ENV.

Writing policy

Nyx core ships the mechanism; rules are registered on the shared engine.

from nyx import Finding, Decision, Rule

# a rule that ENFORCES (acts) while everything else stays shadow:
nyx.policy().add_rule(Rule(
    name="block-metadata",
    check=lambda ctx: Decision.block("cloud metadata egress", policy="block-metadata")
        if (ctx.request.get("host") or "").startswith("169.254.") else None,
    mode="enforce", fail="open",
))

# a rule that only watches (would-block logged, never acted on):
nyx.policy().add_rule(Rule(
    name="model-allowlist",
    check=lambda ctx: Decision.block("disallowed model", policy="model-allowlist")
        if ctx.request.get("model") not in ALLOWED else None,
    mode="shadow",
))

# HOLD requires synchronous approval:
nyx.set_approver(lambda ctx, decision: ask_human(ctx))   # -> bool

# detector: annotates the assembled event with findings (sees RAW content; never blocks)
def metadata_egress(event):
    host = (event.get("request") or {}).get("host") or ""
    if event["boundary"] == "effect" and host.startswith("169.254."):
        return Finding("egress.cloud_metadata", severity="critical",
                       message="agent reached cloud metadata endpoint",
                       data={"host": host}, atlas="AML.T0051", owasp="ASI05")
nyx.policy().add_detector(metadata_egress)

See it work

PYTHONPATH=. python examples/chain_demo.py    # prints the reconstructed trace tree
PYTHONPATH=. python examples/shadow_demo.py   # shadow-mode would-block rollup
PYTHONPATH=. python examples/enforce_demo.py  # per-rule: one rule live, one shadow
PYTHONPATH=. python examples/mcp_demo.py       # MCP client + server boundary (needs `pip install mcp`)
PYTHONPATH=. python benchmarks/overhead.py    # measures per-call overhead
PYTHONPATH=. python benchmarks/policy.py      # policy hot path: linear scan vs boundary index
python -m pytest -q                           # 77 tests (two real-MCP tests skip without the SDK)

# co-located engine split: run the engine, point the shim at it
python -m nyx.engine --socket /tmp/nyx.sock --forward console &
NYX_ENGINE_SOCKET=/tmp/nyx.sock python your_agent.py

chain_demo.py output (the trace tree the control plane reconstructs):

trace 2628fa…  (agent=research-agent session=sess-42)
  └─ [model_client]  chose=['fetch_url']                                  @ main (chain_demo.py:90)
  └─ [tool_dispatch]  tool=fetch_url                                      @ main (chain_demo.py:96)
    └─ [effect]  GET 169.254.169.254  !! critical:egress.cloud_metadata   @ tool_body (chain_demo.py:81)

Design guarantees

  • Do no harm. Extractors, hooks, and detectors are sandboxed; the only exception Nyx raises into agent code is NyxBlocked, and only under enforce=True.
  • Fail open. Errors in the enforcement path allow the call (fail_open=True).
  • No recursion. The reporter posts via stdlib urllib inside suppress_instrumentation(), so the effect boundary never observes Nyx's own traffic.
  • Privacy / residency. Metadata-only by default: payloads stay in the deployment boundary (hashed at ship time, opt in per deployment with capture_content=True); secret-keyed values are always redacted; identity is recorded as a fingerprint hash, never the credential.

Metadata

Release files for nyx-monitor 0.1.0

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

Source distribution (sdist)

Source distribution for nyx-monitor 0.1.0
File Size Uploaded Provenance Actions
nyx_monitor-0.1.0.tar.gz 201.0 kB With provenance Details for nyx_monitor-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for nyx-monitor 0.1.0
File Interpreter ABI Platform Actions
nyx_monitor-0.1.0-py3-none-any.whl Python 3 none any Details for nyx_monitor-0.1.0-py3-none-any.whl

Total release size: 251.5 kB

Release files / nyx_monitor-0.1.0.tar.gz

Download URL nyx_monitor-0.1.0.tar.gz
Size 201.0 kB
Tags Source
SHA-256 checksum
How to use checksums
759534cfedb239036e7cb3a9199f6b0a8b31048c80239321fa6751405bc9899e
BLAKE2b-256 checksum
How to use checksums
36b89bc0fd147976ec40c60823a8df03a92e941150c1a5d407829f72c9339354
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 5, 2026.

Transparency log

Release files / nyx_monitor-0.1.0-py3-none-any.whl

Download URL nyx_monitor-0.1.0-py3-none-any.whl
Size 50.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
da7b2803bbf0adc79b55d49d05fc662c3cf851b70de5dd0c3004347c089f0191
BLAKE2b-256 checksum
How to use checksums
4371cfbbdde162588b95d8ec84f5323b7bbe5e60c28b2542f2faa8b466d94c17
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 5, 2026.

Transparency log

Security for nyx-monitor 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for nyx_monitor-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 5, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

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