Skip to content

Configuration Guide

Node Wire is configured primarily through environment variables and a YAML configuration file.

Environment Variables

All secrets and settings are loaded from environment variables. A template is provided at sample.env.

# Linux/macOS/PowerShell
cp sample.env .env

# Windows (CMD)
copy sample.env .env

Required Variables

Variable Description
NW_ALLOWED_CONNECTORS Required. A comma-separated list of connector names to load (e.g., fhir_epic,http_generic). Node Wire defaults to a fail-closed policy: unset or empty loads no connectors, even when they are enabled in config/connectors.yaml.
NW_CONNECTOR_MODULE_PREFIX Optional. Connectors whose entry-point target module does not start with this prefix are skipped with a warning. Default node_wire_; set to "" to disable the check.

Connector Secrets

Section Key Variables When Needed
FHIR Epic EPIC_FHIR_BASE_URL, EPIC_TOKEN_URL, EPIC_CLIENT_ID, EPIC_KID, EPIC_PRIVATE_KEY Epic EHR integration
FHIR Cerner CERNER_FHIR_BASE_URL, CERNER_TOKEN_URL, CERNER_CLIENT_ID, CERNER_KID, CERNER_PRIVATE_KEY, CERNER_SCOPES Cerner EHR integration
Google Drive GOOGLE_DRIVE_SA_JSON, GOOGLE_DRIVE_FOLDER_ID Google Drive connector
SMTP SMTP_HOST, SMTP_PORT, SMTP_USE_TLS, SMTP_USERNAME, SMTP_PASSWORD, FROM_EMAIL Sending emails; relay pinned to env (not request payload)
Slack SLACK_BOT_TOKEN Sending Slack messages
Stripe STRIPE_API_KEY Stripe payments
Salesforce SALESFORCE_INSTANCE_URL, SALESFORCE_TOKEN_URL, SALESFORCE_CLIENT_ID, SALESFORCE_CLIENT_SECRET, SALESFORCE_REFRESH_TOKEN Salesforce CRM integration
LLM / Agent LLM_PROVIDER, GROQ_API_KEY / GROQ_MODEL, NVIDIA_API_KEY / NVIDIA_BASE_URL / NVIDIA_MODEL, OPENROUTER_API_KEY / OPENROUTER_BASE_URL / OPENROUTER_MODEL / OPENROUTER_MODELS, OLLAMA_BASE_URL / OLLAMA_MODEL / OLLAMA_API_KEY (or other provider keys) AI agent / ToolHive / playground LLM switcher (Groq, NVIDIA, OpenRouter, local Ollama). OpenRouter free models log prompts — do not send PHI/confidential data.

Transport & Binding Config

Variable Description Default
MODE Execution mode (API or GRPC). There is no working MODE=MCP — that value starts a stub process (McpServer() then an infinite sleep loop, no JSON-RPC handling) left over from an early proof of concept. Run MCP via python -m agents.mcp_entrypoint instead (see MCP overview). API
PORT Port for the REST API 8000
NW_REST_HOST REST API bind address 127.0.0.1
NW_REST_PLAYGROUND_ENABLED Mount the interactive playground at /playground/ and /scenarios/* when true. Off by default (checkout only: it is not in any wheel or image). The scenarios run connector actions with the server's credentials, so use it for local development with NW_REST_AUTH_DISABLED=true false
NW_REST_PLAYGROUND_ALLOW_WITH_AUTH Allow the playground to mount when REST auth is configured (NW_REST_API_KEY / NW_REST_JWT_SECRET). Without it the playground is not mounted and an error is logged. /scenarios/* then requires credentials; the playground UI does not send them false
NW_MCP_TRANSPORT MCP transport mode (stdio or streamable-http) stdio
NW_MCP_HOST MCP streamable-http bind address 127.0.0.1
NW_MCP_PORT Port for streamable-http MCP 8081
NW_REST_AUTH_DISABLED Disable REST API authentication (local dev only) false
NW_MCP_AUTH_DISABLED Disable MCP authentication (local dev only); default (unset) enforces auth. The legacy NW_MCP_AUTH_ENABLED flag is deprecated. false
NW_MCP_API_KEY Shared secret for MCP API-key auth (set in production) (unset)
NW_MCP_TOOL_MODE list — every tool in tools/list; search — list only nw_search_tools + nw_call_tool and hand out tool schemas on demand (for large connectors). Unknown values fail at startup list
NW_MCP_SCOPE_POLICY_DEFAULT Scope policy when action map has no entry: deny (conventional mcp:<connector>.<action>) or allow (map-only). Exactly allow or deny (any case); any other value fails startup with a message naming the variable deny
NW_MCP_SCOPE_POLICY_STRICT Fail startup if scope policy would be disabled (allow + empty map) false
NW_GRPC_API_KEY Shared secret for gRPC metadata (authorization or x-api-key) (unset)
NW_GRPC_API_KEY_SCOPES Scopes for gRPC API key (same format as NW_MCP_API_KEY_SCOPES) (empty)
NW_GRPC_AUTH_DISABLED Disable gRPC authentication (local dev only; pair with NW_MCP_SCOPE_POLICY_DEFAULT=allow or scoped dev keys) false
NW_GRPC_TLS_CERT_PATH gRPC server TLS certificate (unset)
NW_GRPC_TLS_KEY_PATH gRPC server TLS private key (unset)
NW_GRPC_REQUIRE_TLS Fail startup if TLS credentials are missing false
NW_JWT_AUDIENCE Expected JWT aud claim when any *_JWT_SECRET is set (MCP / REST / gRPC) (required with JWT secret)
NW_JWT_ISSUER Expected JWT iss claim when any *_JWT_SECRET is set (required with JWT secret)
NW_SMTP_ALLOWED_HOSTS Optional comma-separated SMTP relay hostnames permitted for smtp.send_email (recommended for production) (unset = env relay only)
NW_HTTP_GENERIC_ALLOWED_HOSTS Optional egress allowlist for the http_generic connector only (unset)
NW_REST_ALLOWED_HOSTS Egress allowlist for RestConnector / OpenAPI-generated connectors (comma-separated hostnames). Distinct from NW_HTTP_GENERIC_ALLOWED_HOSTS. Required for any base_url (YAML, tenants.yaml or /v1) whose host differs from the connector's spec server; without it such configs are rejected at write time and at call time. (unset)
NW_REST_TRUST_ENV When true, generated REST connectors construct httpx with trust_env=True so HTTPS_PROXY / HTTP_PROXY apply. Default remains SSRF-safe (false); enabling this re-introduces proxy-based egress — the operator is responsible for proxy trust. Redirects stay disabled. false

Authentication & Request Limits

Defaults below are the values in code, not recommendations — see Security Best Practices for what to set in production.

Variable Description Default
NW_REST_API_KEY Shared secret for REST API-key auth. Send as Authorization: Bearer <key> or X-API-Key: <key> (unset)
NW_REST_API_KEY_SCOPES Scopes granted to NW_REST_API_KEY (JSON array, or comma/space-separated) (empty)
NW_MCP_API_KEY_SCOPES Scopes granted to NW_MCP_API_KEY (same format) (empty)
NW_REST_JWT_SECRET HS256 secret for REST JWT ingress auth. Requires NW_JWT_AUDIENCE + NW_JWT_ISSUER (unset)
NW_MCP_JWT_SECRET HS256 secret for MCP JWT ingress auth (unset)
NW_GRPC_JWT_SECRET HS256 secret for gRPC JWT ingress auth (unset)
NW_REST_LOAD_DOTENV Load .env from disk at startup. Set false in production true
NW_REST_MAX_BODY_BYTES Max JSON body on /connectors/*, /scenarios/* and /v1/*, rejected before parsing 10485760 (10 MiB)
NW_REST_TRUSTED_PROXY_HOPS Number of reverse proxies in front of the app, so REST's client-IP fallback cannot be spoofed via X-Forwarded-For. 0 ignores the header 0

Rate Limiting

Two independent limiters. The global token bucket is always on (unless disabled) and is shared by every caller, so one noisy identity can still exhaust it for everyone. The per-identity sliding window is opt-in and isolates callers from each other. Both are node_wire_runtime facilities shared by REST, MCP, and gRPC.

Variable Description Default
NW_RATE_LIMIT_DISABLED Disable the global token bucket entirely false
NW_RATE_LIMIT_BURST Global bucket capacity 50
NW_RATE_LIMIT_REFILL_RATE Global bucket refill, tokens per second 10.0
NW_RATE_LIMIT_PER_IDENTITY_ENABLED Enable the per-identity sliding-window limiter false
NW_BREAKER_<CONNECTOR_ID>_FAIL_MAX Failed invocations that open that connector's circuit breaker, per tenant instance. <CONNECTOR_ID> is upper-cased with non-alphanumerics as _ (http_generic is HTTP_GENERIC). A connector can also set the breaker_fail_max class attribute; the variable wins. Only upstream-health failures count (see Domain) 5
NW_BREAKER_<CONNECTOR_ID>_RESET_TIMEOUT Seconds the breaker stays open before one trial call is allowed. Class attribute: breaker_reset_timeout 30
NW_RATE_LIMIT_PER_IDENTITY_MAX_REQUESTS Requests allowed per window, per identity 120
NW_RATE_LIMIT_PER_IDENTITY_WINDOW_SECONDS Window size in seconds 60
NW_RATE_LIMIT_PER_IDENTITY_MAX_TRACKED_KEYS LRU cap on tracked identities (bounds memory) 10000
NW_RATE_LIMIT_PER_IDENTITY_KEY_TTL_SECONDS Idle eviction for a tracked identity (bounds memory) 3600

How callers are keyed:

Transport Bucket key Fallback
REST API-key / JWT fingerprint when auth is enabled client IP (honours NW_REST_TRUSTED_PROXY_HOPS)
MCP authenticated principal one shared per-transport bucket
gRPC authenticated principal one shared per-transport bucket

The REST-only NW_REST_RATE_LIMIT_ENABLED family (_MAX_REQUESTS, _WINDOW_SECONDS, _MAX_TRACKED_KEYS, _KEY_TTL_SECONDS) still works as a deprecated alias; the canonical NW_RATE_LIMIT_PER_IDENTITY_* names take precedence when both are set.

Multi-tenancy

Variable Description Default
NW_MULTITENANCY_ENABLED When true, resolve tenant from header / NW_TENANT_ID / JWT and require a tenant (missing → error). When false, always __default__. false
NW_TENANT_ID MCP stdio only — default process pin. Chat can override via nw_select_tenant unless NW_MCP_TENANT_PIN_LOCKED=true. Do not set on multi-tenant streamable-http (use X-Tenant-ID instead). (unset)
NW_TENANT_ID_HEADER HTTP/gRPC header name for tenant id (case-insensitive) X-Tenant-ID
NW_ALLOW_LOCAL_SECRET_FILE true enables the encrypted local secret file (development). Without a secret store, /v1 secret writes fail with 400. See Tenancy. false
NW_LOCAL_SECRET_KEY Fernet key that encrypts the local secret file. Required with NW_ALLOW_LOCAL_SECRET_FILE. unset
NW_LOCAL_SECRET_FILE Absolute path of the local secret file, outside the source tree. Required with NW_ALLOW_LOCAL_SECRET_FILE. unset
NW_API_KEYS_FILE JSON file of tenant-bound API keys [{key_id, hash, tenant, scopes}] (SHA-256 hashes), used by REST, gRPC and MCP. See Tenancy. unset
NW_TRUSTED_TENANT_HEADER true when an authenticating gateway sets the tenant header, so tenant-less credentials may use it. Required for the shared API key in multi-tenant mode unless NW_API_KEYS_FILE is set. false
NW_TENANTS_PATH Path to the YAML file that persists runtime named configs + tenant secret overlays (config/tenants.yaml by default; gitignored). Loaded by REST, gRPC, and standalone MCP (McpServer / agents.mcp_entrypoint) at startup. config/tenants.yaml
NW_MCP_ALLOWED_TENANTS Comma-separated tenant ids the MCP server may list or select. Empty = all tenants that have configs. (unset)
NW_MCP_TENANT_PIN_LOCKED When true, reject nw_select_tenant (pin always wins). false
NW_PROXY_SECRET stacklok-built servers only. Shared secret the ToolHive tenant proxies send as X-NW-Proxy-Secret; when set, a request without the matching header cannot claim a tenant. The generated placeholder REPLACE_ME_PROXY_SECRET is always rejected. Set from deploy/proxy-secret.yaml (see stacklok MCP servers). (unset = not checked)

How these combine (tenant precedence per transport, entitlement, tenant secret naming, the MCP tenant/config tools, the pin contract for embedded hosts) is described once, in Tenancy.

nw CLI

Variable Description Default
NW_DEBUG Any value except empty, 0 or false makes nw print the full traceback with an error, like --debug. (unset)
NW_WHEEL_ARCHS Space-separated CPU architectures for Linux wheel builds (nw gen-stacklok, build-packages.sh --cibw-linux/--musllinux), e.g. x86_64 aarch64. the host's architecture

Configuration File (config/connectors.yaml)

This file determines which connectors are enabled and which protocols they are exposed through.

connectors:
  google_drive:
    enabled: true
    exposed_via:
      - rest
      - grpc
      - mcp
  • enabled: Whether to load the connector at startup.
  • exposed_via: List of protocols (rest, grpc, mcp).

Secrets Management

The factory selects a secret provider from NW_SECRET_BACKEND. Env lookups use the key as given, then key.upper() (e.g. my_key then MY_KEY).

Variable Description Default
NW_SECRET_BACKEND Secret provider to use: env or aws_env (see below) env
NW_ENV_SECRET_LEGACY_EMPTY Return "" instead of raising for a missing key. Legacy behaviour — do not use in production false
NW_AWS_SECRETS_MANAGER_SECRET_ID Secret name or ARN. Required when NW_SECRET_BACKEND=aws_env (unset)
AWS_REGION AWS region for aws_env us-east-1

Missing keys raise SecretNotFoundError (fail-closed) unless NW_ENV_SECRET_LEGACY_EMPTY=true. A connector call then fails with SECRET_NOT_FOUND (TENANT_SECRET_NOT_FOUND for a tenant's secret), category FATAL: the server is misconfigured, so REST answers 500, not 401.

Secret backend (NW_SECRET_BACKEND)

Value Behavior
env (default) Reads from process environment. Raises SecretNotFoundError for absent keys (fail-closed).
aws_env Tries AWS Secrets Manager JSON bundle first; falls back to env on SecretNotFoundError. Propagates SecretProviderError immediately (broken provider is never silently swallowed).

Additional cloud backends (vault, azure, gcp) ship as optional extras in node-wire-runtime but are not currently wired into the factory — using them requires custom composition:

pip install "node-wire-runtime[aws]"    # boto3
pip install "node-wire-runtime[vault]"  # hvac
pip install "node-wire-runtime[azure]"  # azure-keyvault-secrets
pip install "node-wire-runtime[gcp]"    # google-cloud-secret-manager

Google Drive Service Account (Local Example)

For local development, you can set GOOGLE_DRIVE_SA_JSON to the absolute path of your service account JSON file.

PowerShell (Windows):

$saPath = "C:\path\to\service_account.json"
$env:GOOGLE_DRIVE_SA_JSON = Get-Content -Path $saPath -Raw

Bash (Linux/macOS):

export GOOGLE_DRIVE_SA_JSON=$(cat /path/to/service_account.json)


Security Best Practices

  • Production REST: Set NW_REST_API_KEY and send Authorization: Bearer <key> or X-API-Key: <key>.
  • Disable Dotenv: Set NW_REST_LOAD_DOTENV=false in production to prevent loading from a .env file on disk.
  • Fail-Closed: Always explicitly list allowed connectors in NW_ALLOWED_CONNECTORS.
  • Scope policy: Unset NW_MCP_SCOPE_POLICY_DEFAULT defaults to deny in code. Configure NW_MCP_API_KEY_SCOPES, NW_REST_API_KEY_SCOPES, and NW_GRPC_API_KEY_SCOPES (or JWT claims) for each transport. Use NW_MCP_SCOPE_POLICY_DEFAULT=allow only for intentional local fail-open.
  • JWT ingress auth: When using NW_MCP_JWT_SECRET, NW_REST_JWT_SECRET, or NW_GRPC_JWT_SECRET, set NW_JWT_AUDIENCE and NW_JWT_ISSUER. Minted tokens must include exp, iat, aud, and iss (HS256; asymmetric RS256 is not yet supported for bindings).
  • Log redaction: A platform-wide logging filter redacts PHI-like field names and values (for example search_params, body, patient identifiers). FHIR connectors log operation mode, HTTP status, and counts only—not request parameters or raw FHIR response bodies.
  • Rate limiting: The always-on global bucket is shared by every caller, so it only gives coarse DoS protection. Set NW_RATE_LIMIT_PER_IDENTITY_ENABLED=true to isolate callers from each other, and set NW_REST_TRUSTED_PROXY_HOPS to your reverse-proxy depth so the REST IP fallback is not spoofable. Full reference: Rate Limiting.
  • REST body size: Set NW_REST_MAX_BODY_BYTES (default 10 MiB) to cap JSON bodies on /connectors/*, /scenarios/* and /v1/* before handlers parse them. Also set client_max_body_size (or equivalent) on your reverse proxy for defense in depth.
  • Config admin API (/v1): every route needs a scope in the caller's JWT scopes or in NW_REST_API_KEY_SCOPES. Config reads (including secret key names) need nw:config:read; config create/update/delete/set-default need nw:config:write; secret writes, including secrets sent inline with a config, need nw:secrets:write. POST /v1/config/init for one tenant (with X-Tenant-ID) needs nw:config:write; the headerless full init and GET /v1/tenants need nw:platform:admin. nw:platform:admin satisfies every scope; the connector wildcard * does not. Without admin, a caller acts only on its JWT tenant_id, so API keys, tenantless JWTs and single-tenant (__default__) writes need admin. Each change logs one audit_event=config_change line (principal, tenant, connector, config and the number of secret keys written; never names or values).
  • Network bindings: MCP streamable-http defaults to NW_MCP_HOST=127.0.0.1; set 0.0.0.0 only when intentionally exposing beyond localhost. For gRPC, set NW_GRPC_TLS_CERT_PATH and NW_GRPC_TLS_KEY_PATH, or enable NW_GRPC_REQUIRE_TLS=true in production to refuse plaintext startup. Terminate TLS at a reverse proxy if not terminating in-process.