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.
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. |
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 (or other provider keys) |
AI agent / ToolHive / playground LLM switcher |
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.md). |
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/ when true; when unset, enabled only if a playground/ directory exists at the repo root |
(auto) |
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_SCOPE_POLICY_DEFAULT |
Scope policy when action map has no entry: deny (conventional mcp:<connector>.<action>) or allow (map-only) |
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 |
Optional egress allowlist for RestConnector / OpenAPI-generated connectors (comma-separated hostnames). Distinct from NW_HTTP_GENERIC_ALLOWED_HOSTS. |
(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 |
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_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 |
Named-tenant secrets use NW_{TENANT}_{CONNECTOR}_{KEY} for the default config, or NW_{TENANT}_{CONNECTOR}_{CONFIG}_{KEY} for a named config (one credential vault per named config). MCP transport details: mcp-servers.md.
When multitenancy is enabled, MCP exposes nw_list_tenants, nw_select_tenant (returns configs), nw_list_configs, and nw_select_config. nw_select_config's selection applies to every connector on that MCP process (stdio and streamable-http) by default, though a per-call config_name tool argument can override it for a single call. Tenant pin precedence differs by transport: on stdio, nw_select_tenant overrides the NW_TENANT_ID env pin for later calls, unless NW_MCP_TENANT_PIN_LOCKED=true. On streamable-http, the live per-request X-Tenant-ID header (or JWT tenant claim) always wins on every request — a prior nw_select_tenant call can never shadow another concurrent session's request-level tenant. Provision configs via playground REST / YAML — not via MCP.
Host / factory contract: Resolve the request tenant once (resolve_tenant_id in bindings, or your own auth in an embedded app), then pass that id to ConnectorFactory.get(tenant_id=...). Omitting tenant_id on get always resolves __default__ — never the current HTTP/MCP tenant. After get, the connector instance is pinned: run() may omit tenant_id (uses the pin); a conflicting run(tenant_id=...) returns TENANT_MISMATCH (ErrorCategory.AUTH) without executing the action.
Configuration File (config/connectors.yaml)¶
This file determines which connectors are enabled and which protocols they are exposed through.
- enabled: Whether to load the connector at startup.
- exposed_via: List of protocols (
rest,grpc,mcp).
Secrets Management¶
The factory uses an EnvSecretProvider by default. It looks up keys exactly as provided, and then in uppercase (e.g., my_key then MY_KEY).
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):
Security Best Practices¶
- Production REST: Set
NW_REST_API_KEYand sendAuthorization: Bearer <key>orX-API-Key: <key>. - Disable Dotenv: Set
NW_REST_LOAD_DOTENV=falsein production to prevent loading from a.envfile on disk. - Fail-Closed: Always explicitly list allowed connectors in
NW_ALLOWED_CONNECTORS. - Scope policy: Unset
NW_MCP_SCOPE_POLICY_DEFAULTdefaults to deny in code. ConfigureNW_MCP_API_KEY_SCOPES,NW_REST_API_KEY_SCOPES, andNW_GRPC_API_KEY_SCOPES(or JWT claims) for each transport. UseNW_MCP_SCOPE_POLICY_DEFAULT=allowonly for intentional local fail-open. - JWT ingress auth: When using
NW_MCP_JWT_SECRET,NW_REST_JWT_SECRET, orNW_GRPC_JWT_SECRET, setNW_JWT_AUDIENCEandNW_JWT_ISSUER. Minted tokens must includeexp,iat,aud, andiss(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. - Per-identity rate limiting (REST, MCP, gRPC): Off by default; a single global token bucket (
NW_RATE_LIMIT_BURST/NW_RATE_LIMIT_REFILL_RATE, disable viaNW_RATE_LIMIT_DISABLED=true) always applies on top of it for coarse DoS protection, but that bucket is shared by every caller — one noisy/malicious identity can still exhaust it for everyone else. Enable the opt-in per-identity sliding-window limiter withNW_RATE_LIMIT_PER_IDENTITY_ENABLED=trueto isolate callers from each other; it's anode_wire_runtimefacility shared by all three transports; tune it withNW_RATE_LIMIT_PER_IDENTITY_MAX_REQUESTS,NW_RATE_LIMIT_PER_IDENTITY_WINDOW_SECONDS,NW_RATE_LIMIT_PER_IDENTITY_MAX_TRACKED_KEYS, andNW_RATE_LIMIT_PER_IDENTITY_KEY_TTL_SECONDS(the last two bound memory). REST keys buckets by API key/JWT fingerprint when auth is enabled, falling back to client IP for unauthenticated traffic; MCP and gRPC key by the authenticated principal, falling back to a shared per-transport bucket when no identity is available. SetNW_REST_TRUSTED_PROXY_HOPSto the number of reverse proxies in front of the app (e.g.1behind nginx/ALB) so REST's IP fallback isn't spoofable viaX-Forwarded-For; leave at0to ignore it. The legacyNW_REST_RATE_LIMIT_ENABLED(and its_MAX_REQUESTS/_WINDOW_SECONDS/_MAX_TRACKED_KEYS/_KEY_TTL_SECONDSsiblings) still work as a deprecated, REST-only alias for backward compatibility; the canonicalNW_RATE_LIMIT_PER_IDENTITY_*names take precedence when set. - REST body size: Set
NW_REST_MAX_BODY_BYTES(default 10 MiB) to cap JSON bodies on/connectors/*and/scenarios/*before handlers parse them. Also setclient_max_body_size(or equivalent) on your reverse proxy for defense in depth. - Network bindings: MCP streamable-http defaults to
NW_MCP_HOST=127.0.0.1; set0.0.0.0only when intentionally exposing beyond localhost. For gRPC, setNW_GRPC_TLS_CERT_PATHandNW_GRPC_TLS_KEY_PATH, or enableNW_GRPC_REQUIRE_TLS=truein production to refuse plaintext startup. Terminate TLS at a reverse proxy if not terminating in-process.