Add dual User-Agent and X-Envoy-Client-Info headers#110
Merged
raghav-envoy merged 2 commits intomasterfrom Feb 6, 2026
Merged
Conversation
cea51f6 to
6a8244d
Compare
rto-envoy
previously approved these changes
Feb 5, 2026
## Summary
Implement industry-standard dual-header approach (User-Agent + X-Envoy-Client-Info)
for client identification, following patterns from Stripe, AWS, OpenAI, and Twilio SDKs.
## ABSOLUTE GUARANTEES
**GUARANTEE 1: SDK initialization NEVER fails due to User-Agent headers**
- All header generation wrapped in triple-nested try-catch blocks
- Primary attempt → Secondary fallback → Tertiary fallback (no headers)
- Each layer independently catches and handles ALL exceptions
**GUARANTEE 2: All failures are SILENT - no customer log pollution**
- Zero console.error calls in production code
- No assumptions about customer environment (NODE_ENV, etc.)
- Errors are completely invisible to SDK users
**GUARANTEE 3: Meaningful fallbacks at every layer**
- package.json version fails → 'unknown'
- process.version fails → 'unknown'
- os.platform() fails → 'unknown'
- JSON.stringify fails → hardcoded valid JSON string
- Header setting fails → minimal valid headers or no headers
**GUARANTEE 4: User-Agent is telemetry, not critical functionality**
- Authorization header (critical) is NOT wrapped in error handling
- User-Agent headers (telemetry) are completely best-effort
- SDK remains 100% functional without User-Agent headers
## Headers Implemented
### 1. Standard User-Agent (Universal Compatibility)
```
envoy-integrations-sdk/2.4.4 node/18.0.0 [CustomApp/1.0.0]
```
### 2. X-Envoy-Client-Info (Rich Telemetry - JSON)
```json
{
"sdk": "envoy-integrations-sdk",
"version": "2.4.4",
"runtime": "node",
"runtimeVersion": "18.0.0",
"platform": "darwin",
"application": "CustomApp/1.0.0"
}
```
## Error Handling Architecture
### Layer 1: Helper Functions
- `getNodeVersion()`: Returns 'unknown' on any error
- `getPlatform()`: Returns 'unknown' on any error
- Module-level version loading: Defaults to 'unknown'
### Layer 2: Build Functions
- `buildUserAgent()`: Try-catch → Returns 'envoy-integrations-sdk/unknown node/unknown'
- `buildClientInfo()`: Try-catch → Returns minimal ClientInfo object
- `buildClientInfoHeader()`: Try-catch → Returns hardcoded valid JSON string
### Layer 3: Constructor
- Primary: Call build functions
- Secondary: Set minimal fallback headers ('envoy-integrations-sdk/unknown')
- Tertiary: Continue without headers if even fallbacks fail
## Error Scenarios Covered
✅ package.json not found or unreadable
✅ package.json.version missing or malformed
✅ process.version throws exception
✅ process.version missing/undefined
✅ process.version.replace() fails
✅ os.platform() throws exception
✅ JSON.stringify() fails
✅ customUserAgent malformed or contains non-ASCII
✅ axios.defaults.headers assignment fails
✅ Multiple simultaneous failures
✅ Unknown/future edge cases (caught by outer try-catch)
## Implementation Details
### New Files
- `src/util/userAgent.ts` (145 lines)
- buildUserAgent() - never throws
- buildClientInfo() - never throws
- buildClientInfoHeader() - never throws
- getNodeVersion() - never throws
- getPlatform() - never throws
### Modified Files
- `src/base/EnvoyAPI.ts`
- Constructor accepts EnvoyAPIOptions | string (backward compatible)
- Triple-nested error handling for header setting
- Headers set automatically with meaningful fallbacks
- `src/index.ts`
- Export EnvoyAPIOptions type
- Export userAgent utility functions
### New Test Files
- `test/util/userAgent.test.ts` (19 tests)
- `test/base/EnvoyAPI.test.ts` (29 tests)
## Test Coverage
**62 tests total - all passing ✅**
### userAgent utilities (19 tests)
- buildUserAgent with/without custom app
- buildClientInfo with different platforms
- buildClientInfoHeader JSON validation
- Error handling (missing process.version, os.platform errors)
- Edge cases (empty strings, special characters)
- Functions never throw guarantee
### EnvoyAPI constructor (29 tests)
- Backward compatibility (string parameter)
- New options object parameter
- Header setting verification
- Format validation (regex, JSON structure)
- Error resilience (SDK initialization succeeds despite errors)
- Authorization header always succeeds
- Fallback headers used on errors
### Existing tests (14 tests)
- All existing axiosConstructor tests pass
- No regressions introduced
## Usage
### Legacy (Still Works) ✅
```typescript
const client = new EnvoyUserAPI('access-token');
// Headers automatically set with defaults
```
### New (Optional Custom Identifier)
```typescript
const client = new EnvoyUserAPI({
accessToken: 'access-token',
userAgent: 'AcmePortal/2.1.0'
});
// Headers include custom application identifier
```
## Backward Compatibility
✅ 100% backward compatible
✅ Zero breaking changes
✅ Existing code works unchanged
✅ String constructor still supported
✅ All child classes (EnvoyUserAPI, EnvoyPluginAPI) inherit compatibility
## Industry Research
Analyzed User-Agent patterns from:
- **Stripe SDK**: appInfo + JSON-encoded metadata
- **AWS SDK v3**: Middleware-based with customizable provider
- **OpenAI SDK**: defaultHeaders configuration
- **Twilio SDK**: userAgentExtensions parameter
Our dual-header approach combines best practices from all four.
## Benefits
### For Envoy
- Track SDK version adoption
- Debug customer issues faster
- Platform analytics (Node.js versions, OS distribution)
### For Customers
- Identify applications in API usage
- Better support with full context
- Industry-standard pattern (familiar)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
6a8244d to
e5b4c22
Compare
rto-envoy
previously approved these changes
Feb 5, 2026
JustWalters
previously approved these changes
Feb 6, 2026
Address feedback from PR review:
1. Remove unused error parameters from catch blocks
- Changed `} catch (error) {` to `} catch {` throughout userAgent.ts
- Cleaner syntax when error is not used
2. Remove userAgent utility exports from public API
- Removed `export * from './util/userAgent'` from index.ts
- These are internal utilities, not part of public SDK interface
- Kept EnvoyAPIOptions type export for public use
3. Update documentation to remove "legacy" terminology
- Constructor JSDoc now treats both signatures as equal alternatives
- Changed "Legacy usage" to "Simple usage with access token only"
- Changed "New usage" to "Usage with custom User-Agent"
- Updated inline comment from "legacy" to neutral language
4. Add test for emoji and unexpected characters
- New test verifies SDK handles unusual characters gracefully
- Tests emoji (🚀) and non-ASCII characters (中文)
- Ensures JSON serialization works correctly with special chars
All 63 tests pass.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
15a3c98
JustWalters
approved these changes
Feb 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implement industry-standard multi-header approach for client identification, following patterns from leading SaaS SDKs including Stripe, AWS, OpenAI, and Twilio.
This PR adds User-Agent and X-Envoy-Client-Info headers with bulletproof error handling that provides absolute guarantees: SDK initialization never fails, all failures are silent, and meaningful fallbacks are used at every layer.
🛡️ ABSOLUTE GUARANTEES
✅ GUARANTEE 1: SDK Initialization NEVER Fails
✅ GUARANTEE 2: All Failures Are Silent
✅ GUARANTEE 3: Meaningful Fallbacks Everywhere
'unknown''unknown''unknown'✅ GUARANTEE 4: Telemetry Never Blocks Critical Functionality
What This PR Does
Implements industry-standard dual-header approach for client identification:
1️⃣ Standard User-Agent Header
2️⃣ X-Envoy-Client-Info Header (JSON)
{ "sdk": "envoy-integrations-sdk", "version": "2.5.0", "runtime": "node", "runtimeVersion": "18.0.0", "platform": "darwin", "application": "MyApp/1.0.0" }Error Handling Architecture
3-Layer Defense System
Error Scenarios Covered ✅
Industry Best Practices Research
We analyzed how 4 leading SaaS companies implement User-Agent headers in their Node.js SDKs:
1. Stripe SDK (stripe-node)
What They Do:
X-Stripe-Client-User-Agent(JSON)appInfoparameter to identify integrator applicationsCode Example:
What We Learned:
Links:
2. AWS SDK v3 (aws-sdk-js-v3)
What They Do:
defaultUserAgentProviderCode Example:
What We Learned:
Links:
3. OpenAI SDK (openai-node)
What They Do:
defaultHeadersfor global header customizationCode Example:
What We Learned:
Links:
4. Twilio SDK (twilio-node)
What They Do:
userAgentExtensionsarray parameterCode Example:
What We Learned:
Links:
Our Implementation: Best of All Four
We combined the best patterns from all four SDKs:
userAgentparameter for customer appsWhy This Approach:
Usage
Backward Compatible (Zero Breaking Changes) ✅
What Gets Set Automatically
Without custom userAgent:
With custom userAgent:
Test Coverage
62 Tests - All Passing ✅
$ npm test PASS test/util/userAgent.test.ts (19 tests) PASS test/base/EnvoyAPI.test.ts (29 tests) PASS test/util/axiosConstructor.test.ts (14 tests) Test Suites: 3 passed, 3 total Tests: 62 passed, 62 total ✅Test Categories
userAgent utilities (19 tests)
EnvoyAPI constructor (29 tests)
Existing tests (14 tests)
Benefits
For Envoy Engineering 🔧
Version Tracking
Debugging & Support
Platform Analytics
For Customers 👥
Application Identification
Enhanced Support
Industry Standard
Implementation Details
Files Changed
New Files:
✅
src/util/userAgent.ts(145 lines)✅
test/util/userAgent.test.ts(330 lines, 19 tests)✅
test/base/EnvoyAPI.test.ts(354 lines, 29 tests)Modified Files:
✅
src/base/EnvoyAPI.ts(+63 lines)EnvoyAPIOptions | string✅
src/index.ts(+3 lines)EnvoyAPIOptionstype✅
package.json&package-lock.jsonAPI Surface
New Exported Type:
New Exported Functions:
Before/After Comparison
Before This PR
After This PR
Related Work
Checklist
🤖 Generated with Claude Code
Key Takeaway: This PR adds valuable telemetry headers following industry-standard patterns from Stripe, AWS, OpenAI, and Twilio, with absolute guarantees that header generation will never break SDK initialization through bulletproof error handling.