Agent-embedded security monitor. Instruments the four agent boundaries (model-client, framework tool-dispatch, MCP, effect) and reports findings to a control plane.
Project description
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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nyx_monitor-0.1.0.tar.gz.
File metadata
- Download URL: nyx_monitor-0.1.0.tar.gz
- Upload date:
- Size: 201.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
759534cfedb239036e7cb3a9199f6b0a8b31048c80239321fa6751405bc9899e
|
|
| MD5 |
b3984b21be3e20e2d57639580c9a8a76
|
|
| BLAKE2b-256 |
36b89bc0fd147976ec40c60823a8df03a92e941150c1a5d407829f72c9339354
|
Provenance
The following attestation bundles were made for nyx_monitor-0.1.0.tar.gz:
Publisher:
release.yml on kgalappatti-aegis/Nyx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nyx_monitor-0.1.0.tar.gz -
Subject digest:
759534cfedb239036e7cb3a9199f6b0a8b31048c80239321fa6751405bc9899e - Sigstore transparency entry: 2081504729
- Sigstore integration time:
-
Permalink:
kgalappatti-aegis/Nyx@63141fcb5077a81dd9c7c7dfbe5594d65b73b123 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/kgalappatti-aegis
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@63141fcb5077a81dd9c7c7dfbe5594d65b73b123 -
Trigger Event:
push
-
Statement type:
File details
Details for the file nyx_monitor-0.1.0-py3-none-any.whl.
File metadata
- Download URL: nyx_monitor-0.1.0-py3-none-any.whl
- Upload date:
- Size: 50.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
da7b2803bbf0adc79b55d49d05fc662c3cf851b70de5dd0c3004347c089f0191
|
|
| MD5 |
671ddaca838e04298f88d6ccc1389a60
|
|
| BLAKE2b-256 |
4371cfbbdde162588b95d8ec84f5323b7bbe5e60c28b2542f2faa8b466d94c17
|
Provenance
The following attestation bundles were made for nyx_monitor-0.1.0-py3-none-any.whl:
Publisher:
release.yml on kgalappatti-aegis/Nyx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nyx_monitor-0.1.0-py3-none-any.whl -
Subject digest:
da7b2803bbf0adc79b55d49d05fc662c3cf851b70de5dd0c3004347c089f0191 - Sigstore transparency entry: 2081504752
- Sigstore integration time:
-
Permalink:
kgalappatti-aegis/Nyx@63141fcb5077a81dd9c7c7dfbe5594d65b73b123 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/kgalappatti-aegis
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@63141fcb5077a81dd9c7c7dfbe5594d65b73b123 -
Trigger Event:
push
-
Statement type: