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 atproducts/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-levelServer, + 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 —
contextvarsspan chain: effects nest under the tool span that triggered them; whole chain shares atrace_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.systemevents carry an explicitcoverage_gap(execution leaves the interpreter) - ✅ Framework mapping — findings carry version-stamped ATLAS / OWASP Agentic ids
- ✅ Hardened L1 patching — uses
wraptwhen installed (descriptor/signature-safe, survives version drift), hardenedfunctools+setattrfallback 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
UDSSinkships telemetry to a separatepython -m nyx.engineprocess 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 viaPOST /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 indocs/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 underenforce=True. - Fail open. Errors in the enforcement path allow the call (
fail_open=True). - No recursion. The reporter posts via stdlib
urllibinsidesuppress_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)
| 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)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| 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 logRelease 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 logSecurity 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.
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 logA 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.