@arnaude/source-tag
v0.4.0
Published
Zero-touch outbound service-identity headers for Node HTTP clients.
Downloads
169
Maintainers
Readme
IMP
Works Reliably
Node services started with
node -r dotenv/config -r source-tag ...
Common HTTP clients using Node transports:
http https request axios node-fetch built-in fetch most REST SDKs built on these
Local, staging, prod, Docker, PM2, npm scripts, as long as preload is present in the actual process
Does Not Automatically Work
Child processes unless they also preload source-tag Calls made by another container, proxy, sidecar, gateway, or service mesh Non-HTTP protocols Native/custom networking code Libraries that bypass Node HTTP/HTTPS/fetch/http2/undici internals Services not started through the edited package.json script Existing explicit X-Source-Service header, because source-tag preserves caller-provided values Must Verify Per Service
Start service Hit outbound echo endpoint Confirm echo receives: X-Source-Service:
Also include a tiny checklist:
npm i source-tag SERVICE_NAME=my-service node -r dotenv/config -r source-tag dist/server.js
Then test with echo.
source-tag
Zero-touch outbound service-identity headers for supported Node.js HTTP clients.
Preload once, and outbound HTTP calls made through supported Node.js transports will automatically carry a service identity header:
X-Source-Service: <service-name>No per-request instrumentation is required in application code.
Quick Start
SERVICE_NAME=my-service node -r source-tag server.jsFor TypeScript services using ts-node:
SERVICE_NAME=my-service ts-node -r source-tag src/index.tsIf your service loads environment variables from a .env file, preload dotenv before source-tag so SERVICE_NAME is available when source-tag initializes:
node -r dotenv/config -r source-tag server.jsFor a local folder preload:
node -r dotenv/config -r ./source-tag server.jsCompatibility
Minimum supported version:
- Node.js 14
Actively tested in CI against:
- Node.js 14
- Node.js 16
- Node.js 18
- Node.js 20
- Node.js 22
Features such as globalThis.fetch are only patched when available in the current runtime.
Newer Node.js releases may work but are not guaranteed until added to the compatibility test matrix.
What It Does
source-tag patches supported outbound HTTP transports inside the current Node.js process.
When an outbound request is made, it adds this header unless the caller has already set it:
X-Source-Service: <service-name>If the caller already set the source header, that value is preserved.
The check is case-insensitive, so these are treated as the same header:
X-Source-Service
x-source-serviceService Name Resolution
source-tag resolves the service name from the first value it finds, in this order:
| Priority | Source |
| -------- | --------------------------------------------------------------- |
| 1 | SERVICE_NAME environment variable |
| 2 | SOURCE_SERVICE environment variable |
| 3 | npm_package_name environment variable |
| 4 | name field in package.json at the current working directory |
If none of these resolve to a value, source-tag logs a warning and exits without patching anything.
In strict mode, a missing service name is treated as a startup failure.
Coverage
source-tag patches supported outbound HTTP transports at the Node.js runtime level.
| Transport | Covered |
| ------------------------------------------------------------ | ----------------------------------------------- |
| http.request, http.get | Yes |
| https.request, https.get | Yes |
| Axios | Yes, via Node HTTP/HTTPS |
| Got | Yes, via Node HTTP/HTTPS |
| node-fetch v2 | Yes, via Node HTTP/HTTPS |
| SuperAgent | Yes, via Node HTTP/HTTPS |
| Needle | Yes, via Node HTTP/HTTPS |
| Request | Yes, via Node HTTP/HTTPS |
| globalThis.fetch | Yes, when available |
| HTTP/2 sessions via http2.connect | Yes |
| Undici request, stream, pipeline, connect, upgrade | Yes, if Undici is installed |
| Undici dispatcher/client paths | Best effort via Dispatcher.prototype.dispatch |
Known Limitations
source-tag covers supported Node.js HTTP transports in the current process. It does not guarantee coverage for every possible outbound network call.
It does not cover:
- Child processes unless they also preload source-tag
- Non-HTTP protocols
- Custom native networking code
- Traffic sent by another service, sidecar, proxy, or container
- Libraries that completely bypass Node's HTTP, HTTPS, Fetch, HTTP/2, and Undici paths
The service name and header name are resolved once at preload time.
If the module is loaded multiple times with different configurations, the first active configuration wins.
For HTTP/2, header names are sent in lowercase as required by the protocol:
x-source-service: my-serviceConfiguration
All configuration is provided through environment variables.
| Variable | Required | Default | Purpose |
| -------------------- | ----------- | -------------------------- | ---------------------------------------------- |
| SERVICE_NAME | Recommended | Falls back to package name | Value placed in the source header |
| SOURCE_SERVICE | No | None | Alternative service-name variable |
| SOURCE_HEADER | No | X-Source-Service | Override the header name |
| DISABLE_SOURCE_TAG | No | Unset | Disable source-tag entirely |
| SOURCE_TAG_STRICT | No | Unset | Fail startup on critical initialization errors |
| SOURCE_TAG_DISABLE_HTTP | No | Unset | Disable http and https patching |
| SOURCE_TAG_DISABLE_FETCH | No | Unset | Disable globalThis.fetch patching |
| SOURCE_TAG_DISABLE_HTTP2 | No | Unset | Disable HTTP/2 patching |
| SOURCE_TAG_DISABLE_UNDICI | No | Unset | Disable all Undici patching |
Transport Opt-Outs
Each supported transport can be disabled independently without changing application code:
SOURCE_TAG_DISABLE_HTTP=1
SOURCE_TAG_DISABLE_FETCH=1
SOURCE_TAG_DISABLE_HTTP2=1
SOURCE_TAG_DISABLE_UNDICI=1For an initial canary with only HTTP/HTTPS patching enabled:
SERVICE_NAME=my-service \
SOURCE_TAG_DISABLE_FETCH=1 \
SOURCE_TAG_DISABLE_HTTP2=1 \
SOURCE_TAG_DISABLE_UNDICI=1 \
node -r source-tag server.jsThis keeps http.request, http.get, https.request, and https.get enabled, while leaving Fetch, HTTP/2, and Undici unpatched.
Custom Header Name
SERVICE_NAME=payments-service
SOURCE_HEADER=X-Caller-Service
node -r source-tag server.jsOutbound requests will carry:
X-Caller-Service: payments-serviceFor HTTP/2:
x-caller-service: payments-serviceEmergency Disable
If source-tag causes an issue in any environment, disable it without changing application code:
DISABLE_SOURCE_TAG=1 node -r source-tag server.jsWhen disabled, source-tag does not patch any transports and does not add headers.
For canary rollback, remove any transport-specific opt-outs only after clearing the incident; DISABLE_SOURCE_TAG=1 is the full emergency stop.
Strict Mode
By default, source-tag is fail-open.
If initialization fails, source-tag logs the problem and allows the host application to continue running.
For environments where the source header is mandatory:
SOURCE_TAG_STRICT=1 SERVICE_NAME=my-service node -r source-tag server.jsIn strict mode, startup fails if:
- No service name can be resolved
- The configured header name is invalid
- The resolved service name is not a valid HTTP header value
- A critical transport patch fails
This makes configuration problems visible during deployment instead of silently allowing services to run without source headers.
Header Safety
source-tag validates the configured header name before using it.
Invalid SOURCE_HEADER values fall back to:
X-Source-ServiceIn strict mode, an invalid header name causes startup failure.
The resolved service name is trimmed before use.
CR, LF, and NUL characters are removed to prevent HTTP header injection.
Mutation Safety
source-tag clones request options and nested header containers before adding the source header.
It avoids mutating caller-provided request configuration, including:
- Plain objects
Map- Fetch
Headers - Array-of-tuples headers
- Flat header arrays
This is important for applications that reuse request configurations for retries, telemetry, logging, caching, or multiple outbound requests.
Startup Log
On successful initialization, source-tag writes a structured JSON log line to stdout.
Example:
{
"level": "info",
"time": "2026-06-04T10:23:45.123Z",
"module": "source-tag",
"msg": "active",
"service": "my-service",
"header": "X-Source-Service",
"strict": false,
"transportOptOuts": {
"SOURCE_TAG_DISABLE_HTTP": false,
"SOURCE_TAG_DISABLE_FETCH": false,
"SOURCE_TAG_DISABLE_HTTP2": false,
"SOURCE_TAG_DISABLE_UNDICI": false
},
"patched": ["http", "https", "fetch"],
"alreadyPatched": [],
"skipped": ["undici"],
"failed": []
}Warnings and errors are written to stderr using the same JSON-line format.
Startup Log Fields
| Field | Meaning |
| ---------------- | -------------------------------------------- |
| patched | Transport patched during this initialization |
| alreadyPatched | Transport was already patched |
| skipped | Transport unavailable or not applicable |
| failed | Patch failed |
| transportOptOuts | Per-transport disable flags active at startup |
Package Usage
This package is a preload side-effect module.
Use it with Node's -r flag:
node -r source-tag server.jsor:
node -r ./source-tag server.jssource-tag is not intended to expose a public JavaScript or TypeScript API.
Operational Notes
DISABLE_SOURCE_TAG=1disables the module completelySOURCE_TAG_DISABLE_HTTP=1disableshttpandhttpspatchingSOURCE_TAG_DISABLE_FETCH=1disablesglobalThis.fetchpatchingSOURCE_TAG_DISABLE_HTTP2=1disables HTTP/2 patchingSOURCE_TAG_DISABLE_UNDICI=1disables all Undici patchingSOURCE_TAG_STRICT=1is recommended when source headers are mandatory- Patches are idempotent
- Loading the module multiple times with the same configuration does not patch again
- Loading the module multiple times with different configurations keeps the first active configuration
- Undici support is optional and only enabled when Undici is installed
globalThis.fetchis only patched when available- HTTP/2 header names are automatically lowercased
- The module is process-local; other processes must preload source-tag separately
Local Development Example
For a service named spartacus:
.env
SERVICE_NAME=spartacus
NODE_OPTIONS=-r ./source-tagIf the service uses dotenv:
node -r dotenv/config \
-r ./source-tag \
-r tsconfig-paths/register \
-r ts-node/register \
index.tsInitialization order:
dotenv/configloads environment variablessource-tagresolves configuration and patches transportstsconfig-paths/registerenables TypeScript path aliasests-node/registerenables TypeScript executionindex.tsstarts the application
Testing
source-tag is actively tested in CI across supported Node.js versions.
Current automated coverage includes:
http.requesthttp.gethttps.requesthttps.getglobalThis.fetch- HTTP/2
- Existing-header preservation
- Case-insensitive header detection
- Strict mode
- Disable mode
- Service-name resolution
- Idempotent initialization
- Mutation safety for supported header formats
Before broad production adoption, verify source-tag with the transports and request patterns used by your own services.
