- Add 'trusted-proxy' to ResolvedGatewayAuthMode
- Add trustedProxy field to ResolvedGatewayAuth
- Add authorizeTrustedProxy() helper function
- Update authorizeGatewayConnect() to handle trusted-proxy mode
- Validate proxy source IP against trustedProxies list
- Support required headers and user allowlist validation
Part of #1560
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added dedicated 'backward compatibility' test suite to verify:
- Exact IP matching still works (no CIDR notation)
- Plain IPs are NOT treated as /32 CIDR (exact match only)
- IPv4-mapped IPv6 normalization preserved (existing normalizeIp behavior)
These tests document that the CIDR matching addition does not break
existing exact-IP configurations and preserves all previous behavior.
The isTrustedProxyAddress() function was doing exact IP matching only,
which broke when trustedProxies contained CIDR notation (e.g., '10.42.0.1/24').
Issue: Pomerium connecting from 10.42.0.59 was rejected even though
trustedProxies contained '10.42.0.1/24'.
Root cause: The function used normalizeIp() comparison which stripped
the /24 suffix and compared '10.42.0.1' === '10.42.0.59' (false).
Fix:
- Added ipMatchesCIDR() helper for IPv4 CIDR matching using bit masks
- Updated isTrustedProxyAddress() to check CIDR blocks when proxy
contains '/' character, otherwise exact IP match
- Supports /0 through /32 prefix lengths
- Validates IP and prefix length format
Tests:
- Exact IP matching (existing behavior)
- CIDR /24, /16, /32 subnet matching
- Mixed exact IPs and CIDR notation in same array
- Edge cases (undefined, empty, invalid CIDR)
This was the ACTUAL bug preventing Docker deployment with Pomerium.
The entrypoint correctly detected and configured trustedProxies with
CIDR notation, but the gateway rejected connections due to this bug.
Added comment to clarify that the lan binding + trusted-proxy test
validates both:
1. CLI startup validation (src/cli/gateway-cli/run.ts line 246)
2. Runtime config validation (src/gateway/server-runtime-config.ts line 99)
Both layers must allow lan binding with trusted-proxy auth mode.
The runtime config test implicitly validates both code paths since
they use the same logic (checking for shared secret).
There were TWO validations for lan binding without auth:
1. src/gateway/server-runtime-config.ts (FIXED in 563052d)
2. src/cli/gateway-cli/run.ts (THIS FIX)
The CLI startup validation was rejecting lan binding when using
trusted-proxy auth mode because it only checked for token/password.
This is the ACTUAL bug causing the crash loop - the CLI validation
runs before the runtime config validation, so it was failing first.
Fix: Add && resolvedAuthMode !== 'trusted-proxy' to the CLI check
on line 246, matching the fix in server-runtime-config.ts.
Add tests to verify that browser control auth doesn't auto-generate
tokens when gateway.auth.mode is 'trusted-proxy' or 'password'.
Covers:
- Trusted-proxy mode: no token generation
- Password mode: no token generation (even if password unset)
- Token mode: respects existing token
- Test environment: skips auto-generation
The browser control service was auto-generating a gateway.auth.token when
no token/password was detected, even when gateway.auth.mode was set to
'trusted-proxy'. This overwrote the trusted-proxy configuration and
switched the gateway back to token mode.
Fix: Skip auto-token generation when auth mode is 'trusted-proxy',
similar to how it already skips for 'password' mode.
This prevents the browser service from mangling the trusted-proxy config
during startup.
Add comprehensive tests for gateway runtime config validation:
- Trusted-proxy mode allows lan binding
- Trusted-proxy mode rejects loopback binding
- Trusted-proxy mode requires trustedProxies configured
- Token mode requires token to be set
- Token mode allows lan binding when token is provided
These tests validate the fix for the lan binding validation bug
and prevent regression.
Critical bug: Gateway startup validation rejected lan binding when using
trusted-proxy auth mode because it only checked for token/password.
The validation on line 99 threw 'refusing to bind gateway to lan without auth'
even when authMode was 'trusted-proxy', because hasSharedSecret is false
for trusted-proxy mode (it doesn't use tokens/passwords).
Fix: Allow lan binding when authMode is 'trusted-proxy' by adding
&& authMode !== 'trusted-proxy' to the condition.
This allows the gateway to start with bind=lan when configured for
trusted-proxy authentication (e.g., behind Pomerium).
Without this fix, users get crash-loop with 'refusing to bind' error
even though trusted-proxy mode is correctly configured.
- Add authMode to gateway snapshot schema (protocol)
- Resolve and expose auth mode in buildGatewaySnapshot()
- Update Control UI overview to conditionally render based on authMode
- When authMode is 'trusted-proxy':
- Hide Gateway Token field
- Hide Password field
- Show informational callout explaining trusted-proxy auth
- Display auth mode and clarify no token needed
- When authMode is 'token', 'password', or 'none':
- Show token and password fields as before (unchanged UX)
Benefits:
- Clearer UX: users won't be confused trying to enter tokens
- Security clarity: makes it obvious proxy handles auth
- Better feedback: explains what trusted-proxy mode means
UI shows:
'This gateway is configured for trusted-proxy auth mode.
User identity is managed by your reverse proxy (Pomerium, Caddy, Traefik, etc.).
Auth Mode: trusted-proxy
No gateway token required — authentication handled by proxy'
- Add tests for buildGatewayAuthConfig with trusted-proxy mode
- Test all trusted-proxy options (userHeader, requiredHeaders, allowUsers)
- Test minimal trusted-proxy config (userHeader only)
- Test preserving allowTailscale when switching to trusted-proxy
- Test error when trustedProxy config missing
- Test dropping token/password when switching to trusted-proxy
- Add integration tests for interactive gateway prompting flow
- Test trusted-proxy with all options and with minimal options
Test coverage:
- 5 new tests in configure.gateway-auth.test.ts
- 2 new tests in configure.gateway.test.ts
All tests verify proper handling of the new trusted-proxy auth mode.
- Test trusted-proxy mode flagged as critical
- Test missing trustedProxies finding
- Test missing userHeader finding
- Test empty allowUsers warning
- Fix env isolation for bind_no_auth test
Part of #1560
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Test valid request from trusted proxy
- Test rejection of untrusted source
- Test missing user header handling
- Test missing required headers
- Test user allowlist enforcement
- Test Pomerium-style headers
- Test whitespace trimming
Part of #1560
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add critical finding when trusted-proxy auth is enabled
- Flag missing trustedProxies configuration
- Flag missing userHeader configuration
- Warn when allowUsers is empty (allows any authenticated user)
Part of #1560
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Allow non-loopback bind with trusted-proxy auth mode
- Reject trusted-proxy + loopback combination (nonsensical)
- Require trustedProxies to be configured for trusted-proxy mode
Part of #1560
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- src/channels/dock.ts: core channel dock fallback
- src/auto-reply/reply/reply-routing.test.ts: test expectation
- docs/zh-CN/channels/telegram.md: Chinese docs reference
Comprehensive grep confirms no remaining Telegram-specific "first"
defaults after this commit.
In 2026.2.13, the combination of implicit reply threading (#14976) and
the existing Telegram default replyToMode="first" causes every bot
response in DMs to be sent as a native Telegram reply (quoted message
bubble), even for simple exchanges like "Hi" → "Hey".
This is a UX regression: prior to 2026.2.13, reply threading was less
consistent so the "first" default rarely produced visible quote bubbles
in DMs. Now that implicit threading works reliably, the default
effectively means every first message in a response gets quoted —
which feels noisy and unexpected in 1:1 conversations.
Changing the default to "off" restores the pre-2026.2.13 DM experience.
Users who want reply threading can still opt in via config:
channels.telegram.replyToMode: "first" | "all"
Tested by toggling replyToMode on a live 2026.2.13 instance:
- replyToMode="first" → every response quotes the user message
- replyToMode="off" → clean responses without quote bubbles
No test changes needed: existing tests explicitly set replyToMode
rather than relying on the default.
Add support for NVIDIA's API (https://integrate.api.nvidia.com/v1) with three models:
- nvidia/llama-3.1-nemotron-70b-instruct (default)
- nvidia/llama-3.3-70b-instruct
- nvidia/mistral-nemo-minitron-8b-8k-instruct
Users can configure via NVIDIA_API_KEY environment variable or auth profiles.
Co-authored-by: thesomewhatyou <162917831+thesomewhatyou@users.noreply.github.com>