Network Egress Policy & Guardrails via AsyncLocalStorage (ASL)
In workflow orchestration systems, governing external network egress and side-effects is critical for security, data privacy, and compliance. Without guardrails, workflow steps executing arbitrary code can contact unauthorized third-party APIs, leak secrets, or establish unmonitored raw TCP sockets. Siglo provides a declarative, least-privilege Network Egress Policy & Guardrail enforced entirely in-process using Node’s standardnode:async_hooks.AsyncLocalStorage (ASL). It requires zero external daemons or sidecars, isolates concurrent steps and branches on the single Node.js event loop, and intercepts egress across fetch, HTTP/HTTPS, and raw sockets.
1. Core Principles & Guarantees
- Policy Activation & Default Deny: Unconfigured workflows have no egress enforcement (unrestricted). When a network policy is declared on a flow or step, enforcement activates and defaults to denying external outbound network access unless explicitly authorized by step-level or flow-level allowlists.
- Flow Supersedes Step (Outer Envelope Narrowing): The Flow defines the outer security perimeter. A Step can only narrow permissions within what the Flow permits; it cannot unilaterally expand or bypass the Flow allowlist.
- Multi-Layer Egress Interception: Outbound requests are governed across three distinct runtime layers:
- Native
globalThis.fetch(Fetch API, Undici) node:httpandnode:https(requestandget, Axios, Got, legacy clients)node:net.Socket.prototype.connect(Raw TCP/TLS connections)
- Native
- Localhost & Loopback Configuration: When network policy is active,
allowLocalhostdefaults tofalse. When enabled (allowLocalhost: true), loopback addresses (localhost,127.0.0.1,::1,0.0.0.0) and local Unix domain socket IPC (allowUnixSockets) are permitted, preserving local database pools, test mock servers, and container-local sidecars. - Zero Concurrency Cross-Talk: Handled via
AsyncLocalStorage. When steps execute concurrently inparallel,allSettled, ormap, each step runs in its own isolated ASL context. Background host operations (e.g. server health checks, connection pool pings) remain unaffected.
2. Architecture & Runtime Context Propagation
2.1 The Concurrency Challenge in Node.js
Because Node.js runs asynchronous tasks concurrently on a single event loop, global state or process-wide patching causes severe race conditions between concurrent workflows or parallel branches.2.2 AsyncLocalStorage Context Isolation
When a step begins inStartChain, Siglo resolves the effective policy between the step’s declaration and the flow’s perimeter, then executes the entire step lifecycle (including Step.spec validation, transformers, translators, schema checks, lifecycle hooks, and the step action) inside networkPolicyStorage.run(policy, ...). All downstream asynchronous operations, child promises, and third-party libraries automatically inherit the active policy store.
3. Step-Level Declarative API
Steps can declare network permissions using intuitive chainable methods on theStep builder.
3.1 step.allowNetwork(hosts)
Permits outbound requests matching specified host patterns during this step’s execution:
3.2 step.denyNetwork()
Strictly disallows all external network calls from the step. Ideal for pure business logic, cryptographic hashing, or sensitive data transformations:
3.3 step.networkPolicy(policy)
Configures complete, fine-grained network settings including localhost exemptions:
4. Flow-Level Declarative API
TheFlow defines the outer security perimeter governing all enclosed steps.
4.1 flow(...).networkPolicy(policy)
Establishes the allowable perimeter for the entire workflow:
4.2 Per-Execution Run Overrides
Override or further restrict network policies dynamically for a specific workflow execution:5. Precedence Rules: Flow Supersedes Step
When both Flow-level and Step-level policies are declared, the Flow sets the boundary:6. Pattern Matching Grammar
Outbound network destinations are matched against configured patterns using standard rules:7. Multi-Layer Interceptors
Siglo activates interceptors lazily upon first workflow execution or viaensureNetworkInterceptorsInstalled():
-
Native
globalThis.fetchInterceptor:- Inspects target input (string,
URL, orRequestobject). - Resolves target host and checks the active ASL store.
- Throws
NetworkPolicyViolationExceptionsynchronously before any socket is created. - Sets an internal validated socket flag so underlying Node HTTP socket pools avoid duplicate verification overhead.
- Inspects target input (string,
-
node:httpandnode:httpsInterceptor:- Intercepts
http.request,http.get,https.request, andhttps.get. - Protects libraries relying on Node’s native HTTP client (e.g. Axios, Got, Node core).
- If blocked, emits an
'error'event withNetworkPolicyViolationExceptionon the client request stream.
- Intercepts
-
node:net.Socket.prototype.connectInterceptor:- Deepest safety net preventing raw TCP/TLS connections to unauthorized endpoints.
- Automatically bypasses local Unix domain socket paths and sockets already validated by higher-level HTTP interceptors.
- Destroys unauthorized sockets immediately with
NetworkPolicyViolationException.
8. Exception & Error Model: NetworkPolicyViolationException
Unauthorized network attempts immediately throw a NetworkPolicyViolationException:
Non-Retryable Classification
Security and policy violations are intentionally tagged with:phase: 'action'(orphase: 'validation'when triggered duringStep.specvalidation, transforms, or translations;phase: 'before'/phase: 'after'in hooks)retryable: false
9. Concurrency & Parallel Branch Isolation
When orchestrating parallel branches withparallel, allSettled, or map, each branch executes in its own isolated AsyncLocalStorage scope:
