Python client for Detrix - debug-on-demand observability with zero overhead
Project description
Detrix Python Client
Debug-on-demand observability for Python applications with zero overhead when inactive.
Overview
The Detrix Python client enables your application to be observed by the Detrix daemon without:
- Code modifications - No print statements or logging changes needed
- Redeployment - Add metrics to running processes
- Performance overhead - Zero cost when not actively observing
Installation
# Using uv (recommended)
uv add detrix-py
# Using pip
pip install detrix-py
Quick Start
1. Start the Detrix daemon
detrix serve --daemon
2. Initialize the client in your application
import detrix
# Initialize client - starts control plane, stays SLEEPING (zero overhead)
detrix.init(
name="my-service",
daemon_url="http://127.0.0.1:8090",
)
# Your application code runs normally...
def process_request(request):
data = transform(request)
return data
3. Enable observability when needed
# Wake up - starts debugger, registers with daemon
detrix.wake()
# Now the daemon can set observation points on any line
# No code changes needed!
# When done observing
detrix.sleep()
Try It
Run the end-to-end example that simulates an AI agent: starts a sample app, wakes it, adds metrics, and captures events.
# 1. Start the Detrix server
detrix serve --daemon
# 2. Run the agent simulation (from clients/python/)
uv run python examples/test_wake.py --daemon-port 8090
Other examples in examples/:
| Example | Description | Run |
|---|---|---|
basic_usage.py |
Init / wake / sleep cycle | uv run python examples/basic_usage.py |
trade_bot_detrix.py |
Long-running app with embedded client | uv run python examples/trade_bot_detrix.py |
test_wake.py |
Agent simulation (starts app, wakes, observes) | uv run python examples/test_wake.py |
API Reference
detrix.init(**kwargs)
Initialize the client. Starts the control plane HTTP server.
Parameters:
name(str): Connection name (default:"detrix-client-{pid}")control_host(str): Control plane host (default:"127.0.0.1")control_port(int): Control plane port (default:0= auto-assign)debug_port(int): debugpy port (default:0= auto-assign on wake)daemon_url(str): Detrix daemon URL (default:"http://127.0.0.1:8090")start_state(str): Initial state"sleeping"or"warm"(default:"sleeping")detrix_home(str): Path to Detrix home directory (default:~/detrix)health_check_timeout(float): Timeout for daemon health checks in seconds (default:2.0)register_timeout(float): Timeout for connection registration in seconds (default:5.0)unregister_timeout(float): Timeout for connection unregistration in seconds (default:2.0)
detrix.status() -> dict
Get current client status.
Returns:
{
"state": "sleeping" | "warm" | "awake",
"name": "my-service-12345",
"control_host": "127.0.0.1",
"control_port": 9000,
"debug_port": 5678, # 0 if never awake
"debug_port_active": True, # True if debug port is actually open
"daemon_url": "http://127.0.0.1:8090",
"connection_id": "my-service-12345", # None if not awake
}
detrix.wake(daemon_url: str = None) -> dict
Start debugger and register with daemon.
Parameters:
daemon_url(str): Override daemon URL (optional)
Raises:
DaemonError: If daemon is not reachable or URL is invalid
Returns:
{"status": "awake", "debug_port": 5678, "connection_id": "my-service-12345"}
detrix.sleep() -> dict
Stop debugger and unregister from daemon.
Returns:
{"status": "sleeping"}
detrix.shutdown()
Stop control server and cleanup. Call init() again to reinitialize.
State Machine
┌──────────┐ wake() ┌───────┐
│ SLEEPING │ ──────────────────────►│ AWAKE │
└──────────┘ └───────┘
▲ │
│ sleep() │
└────────────────────────────────────┘
- SLEEPING: No debugger loaded, zero overhead
- AWAKE: debugpy listening, registered with daemon
Control Plane HTTP Endpoints
The client exposes a local HTTP server for management:
| Endpoint | Method | Description |
|---|---|---|
/detrix/health |
GET | Health check |
/detrix/status |
GET | Get current status |
/detrix/info |
GET | Get process info |
/detrix/wake |
POST | Start debugger + register |
/detrix/sleep |
POST | Stop debugger + unregister |
Environment Variables
| Variable | Description |
|---|---|
DETRIX_NAME |
Default connection name |
DETRIX_CONTROL_HOST |
Control plane host |
DETRIX_CONTROL_PORT |
Control plane port |
DETRIX_DEBUG_PORT |
debugpy port |
DETRIX_DAEMON_URL |
Daemon URL |
DETRIX_TOKEN |
Authentication token |
DETRIX_HOME |
Detrix home directory |
DETRIX_HEALTH_CHECK_TIMEOUT |
Timeout for daemon health checks (secs) |
DETRIX_REGISTER_TIMEOUT |
Timeout for connection registration (secs) |
DETRIX_UNREGISTER_TIMEOUT |
Timeout for connection unregistration (secs) |
Security
Authentication
The control plane HTTP server has a security-first authentication model:
- Localhost requests (127.0.0.1, ::1, localhost) are always allowed without authentication
- Remote requests require a valid Bearer token
To enable remote access:
- Set
DETRIX_TOKENenvironment variable, or - Create
~/detrix/mcp-tokenfile with the token
If no token is configured, remote requests are denied by default.
Remote Exposure Guidelines
The control plane is designed for localhost access only by default. If you need to expose it remotely:
- Always configure authentication - Set
DETRIX_TOKENor create~/detrix/mcp-token - Use a reverse proxy - Place nginx, HAProxy, or similar in front for:
- TLS termination (HTTPS)
- Rate limiting
- Access logging
- IP allowlisting
- Restrict network access - Use firewall rules to limit which hosts can connect
- Protect the token file - Ensure
~/detrix/mcp-tokenhas restrictive permissions (chmod 600)
Example nginx configuration:
location /detrix/ {
# Rate limiting
limit_req zone=detrix burst=10 nodelay;
# Proxy to control plane
proxy_pass http://127.0.0.1:9000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
Note: The control plane exposes debugging capabilities. Unauthorized access could allow an attacker to inspect process state. Always treat it as a sensitive endpoint.
Error Handling
The client provides a hierarchy of exception types:
from detrix import DetrixError, ConfigError, DaemonError, DebuggerError
try:
detrix.wake()
except DaemonError as e:
print(f"Cannot reach daemon: {e}")
except DebuggerError as e:
print(f"Debugger error: {e}")
except DetrixError as e:
print(f"General error: {e}")
DetrixError: Base class for all Detrix errorsConfigError: Configuration/initialization errorsDaemonError: Communication with daemon failedDebuggerError: debugpy-related errorsControlPlaneError: Control plane server errors
Note: DaemonConnectionError is kept as an alias for DaemonError for backward compatibility.
Known Limitations
1. debugpy Port Remains Open After Sleep
Issue: After calling sleep(), the debug port remains open until the process exits.
Cause: This is a limitation of debugpy itself - it does not support stopping its listener once started. See debugpy#895.
Impact:
- The
debug_port_activefield in status remainsTrueafter sleep - Calling
wake()aftersleep()reuses the same port - The port is only freed when the process terminates
Workaround: This is expected behavior. If you need to fully release the port, you must restart the process.
2. Port Allocation Race Condition (Fixed)
Previous issue: Another process could claim a port between detection and binding.
Resolution: The client now passes port=0 directly to debugpy.listen(), which
handles port allocation atomically at bind time. There is no window for another
process to claim the port.
Note: If you explicitly specify a port via debug_port parameter or
DETRIX_DEBUG_PORT, the standard TOCTOU limitation applies (another process
could theoretically bind to that port first). Use port=0 for guaranteed
atomic allocation.
Architecture
For detailed architecture documentation, see ARCHITECTURE.md.
Development
# Install dev dependencies
uv sync --all-extras
# Run tests
uv run pytest
# Type check
uv run mypy src/detrix
# Lint
uv run ruff check src/detrix
License
MIT
Project details
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 detrix_py-1.0.0.tar.gz.
File metadata
- Download URL: detrix_py-1.0.0.tar.gz
- Upload date:
- Size: 99.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.0 {"installer":{"name":"uv","version":"0.10.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ccc9a16132c71bb0f4fcd75b8f6058aacf4730ad5db7c430cc386cfc6fdb558a
|
|
| MD5 |
a1bd0ca645b3cfa5c46981406b2c2d03
|
|
| BLAKE2b-256 |
014e34ab276804398a9791f4fbb592ac2210806c7166ad9ab60f4497232617ed
|
File details
Details for the file detrix_py-1.0.0-py3-none-any.whl.
File metadata
- Download URL: detrix_py-1.0.0-py3-none-any.whl
- Upload date:
- Size: 26.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.0 {"installer":{"name":"uv","version":"0.10.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e35e9121c6088f582e0a92720e470c4af33ac285cce40e666da6989baa3d4516
|
|
| MD5 |
4f6a9c4de55713c8d6d74bf145821860
|
|
| BLAKE2b-256 |
da4a17470f02feb90716df56cb98b900c42e6a9a26d4f5970b51c7bf23380a5f
|