Skip to main content
The proxy system provides domain-filtered, credential-injected network access for sandboxed child processes. The proxy runs in the unsandboxed supervisor process; the sandboxed child connects only to 127.0.0.1 via standard HTTP_PROXY/HTTPS_PROXY environment variables.

ProxyConfig

ProxyConfig(
    allowed_hosts: list[str] | None = None,
    routes: list[RouteConfig] = [],
    external_proxy: ExternalProxyConfig | None = None,
    bind_addr: str = "127.0.0.1",
    bind_port: int = 0,
    max_connections: int = 256,
    intercept_ca_dir: str | None = None,
    intercept_parent_ca_pems: bytes | None = None,
    allow_all_hosts: bool = False,
)
Configuration for the nono network filtering proxy.
ParameterTypeDefaultDescription
allowed_hostslist[str] | NoneNoneTransparent CONNECT host allowlist. None or [] denies transparent CONNECT except for configured route upstreams needed by reverse proxy forwarding. Supports *.domain wildcards.
routeslist[RouteConfig][]Reverse proxy credential injection routes.
external_proxyExternalProxyConfig | NoneNoneEnterprise proxy passthrough configuration.
bind_addrstr"127.0.0.1"Address to bind the proxy to.
bind_portint0Port to bind. 0 = OS-assigned ephemeral port.
max_connectionsint256Maximum concurrent connections. 0 = unlimited.
intercept_ca_dirstr | NoneNoneDirectory containing CA certificates for TLS interception (ConnectIntercept mode).
intercept_parent_ca_pemsbytes | NoneNoneParent CA PEM bytes for generating interception certificates.
allow_all_hostsboolFalseExplicit opt-in to allow transparent CONNECT to all hosts except the hardcoded metadata deny list. Cannot be combined with allowed_hosts.

Example

from nono_py import ProxyConfig, RouteConfig, InjectMode, start_proxy

config = ProxyConfig(
    allowed_hosts=["api.openai.com", "*.anthropic.com"],
    routes=[
        RouteConfig(
            prefix="/openai",
            upstream="https://api.openai.com",
            credential_key="openai-key",
        ),
    ],
)
proxy = start_proxy(config)

RouteConfig

RouteConfig(
    prefix: str,
    upstream: str,
    credential_key: str | None = None,
    inject_mode: InjectMode = InjectMode.HEADER,
    inject_header: str = "Authorization",
    credential_format: str | None = None,
    path_pattern: str | None = None,
    path_replacement: str | None = None,
    query_param_name: str | None = None,
    env_var: str | None = None,
    endpoint_rules: list[tuple[str, str]] = [],
    tls_ca: str | None = None,
    tls_client_cert: str | None = None,
    tls_client_key: str | None = None,
)
Configuration for a reverse proxy credential injection route. When the sandboxed child sends a request to http://127.0.0.1:<port>/<prefix>/..., the proxy forwards it to upstream with real credentials injected.
ParameterTypeDefaultDescription
prefixstrrequiredPath prefix for routing (e.g., "/openai")
upstreamstrrequiredUpstream URL (e.g., "https://api.openai.com")
credential_keystr | NoneNoneOS keyring account name for the credential
inject_modeInjectModeHEADERHow to inject the credential
inject_headerstr"Authorization"Header name (for HEADER mode)
credential_formatstr | NoneNoneFormat string with {credential} placeholder (e.g., "Bearer {credential}")
path_patternstr | NoneNoneURL path mode: pattern to match in incoming path
path_replacementstr | NoneNoneURL path mode: replacement pattern for outgoing path
query_param_namestr | NoneNoneQuery param mode: parameter name
env_varstr | NoneNoneOverride env var name for the phantom token
endpoint_ruleslist[tuple[str, str]][]Per-endpoint method/path allow rules (e.g., [("POST", "/v1/chat/completions")])
tls_castr | NoneNoneCustom CA certificate PEM for upstream TLS verification
tls_client_certstr | NoneNoneClient certificate PEM for mutual TLS
tls_client_keystr | NoneNoneClient private key PEM for mutual TLS

InjectMode

Credential injection method:
ValueDescription
InjectMode.HEADERInject as HTTP header (default)
InjectMode.URL_PATHReplace pattern in URL path
InjectMode.QUERY_PARAMAdd as query parameter
InjectMode.BASIC_AUTHHTTP Basic Authentication

ProxyHandle

Returned by start_proxy(). Not user-constructable.

Properties

PropertyTypeDescription
portintPort the proxy is listening on

Methods

env_vars() -> dict[str, str]

Environment variables to inject into the sandboxed child: HTTP_PROXY, HTTPS_PROXY, NO_PROXY, NONO_PROXY_TOKEN, and lowercase variants.

credential_env_vars() -> dict[str, str]

Per-route base URL overrides and phantom tokens (e.g., OPENAI_BASE_URL, OPENAI_API_KEY). Only includes routes where credentials were loaded from the keyring.

sandbox_env() -> list[tuple[str, str]]

Convenience method combining env_vars() and credential_env_vars() into a single list of (key, value) tuples, ready to pass directly to sandboxed_exec(env=...).

drain_audit_events() -> list[dict]

Drain and return collected network audit events. Each dict contains:
KeyTypeDescription
timestamp_unix_msintEvent timestamp
modestr"connect", "connect_intercept", "reverse", "external"
decisionstr"allow" or "deny"
targetstrHostname or service
portint | NoneTarget port
methodstr | NoneHTTP method (reverse proxy)
pathstr | NoneRequest path (reverse proxy)
statusint | NoneUpstream response status
reasonstr | NoneDenial reason
route_idstr | NoneMatched route prefix
auth_mechanismstr | None"proxy_authorization", "phantom_header", "phantom_path", "phantom_query"
auth_outcomestr | None"succeeded" or "failed"
managed_credential_activebool | NoneWhether a managed credential was used
injection_modestr | None"header", "url_path", "query_param", "basic_auth", "oauth2"
denial_categorystr | None"host_denied", "endpoint_policy", "authentication_failed", etc.

shutdown() -> None

Signal the proxy to shut down gracefully.

Example

proxy = start_proxy(config)

# Pass to sandboxed child
env = proxy.sandbox_env()
result = sandboxed_exec(caps, ["python", "agent.py"], env=env)

# Review what happened
for event in proxy.drain_audit_events():
    print(f"[{event['decision']}] {event['mode']} -> {event['target']}")

proxy.shutdown()

ExternalProxyConfig

Enterprise proxy passthrough for environments behind a corporate proxy.
ExternalProxyConfig(
    address: str,
    bypass_hosts: list[str] = [],
)
ParameterTypeDefaultDescription
addressstrrequiredProxy address (e.g., "squid.corp.internal:3128")
bypass_hostslist[str][]Hosts that bypass the external proxy. Supports *.domain wildcards.

Security Properties

  • Cloud metadata deny list: 169.254.169.254 and equivalents are always blocked
  • DNS rebinding protection: Resolved IPs are validated against link-local ranges
  • Credential isolation: Real API keys never reach the sandboxed process
  • Constant-time token comparison: Prevents timing side-channel attacks
  • Audit logging: Every request logged, sensitive data excluded