Skip to content

feat: Resolve sharded URL at startup#293

Open
clement0010 wants to merge 14 commits into
masterfrom
feat/gateway-multiverse-support
Open

feat: Resolve sharded URL at startup#293
clement0010 wants to merge 14 commits into
masterfrom
feat/gateway-multiverse-support

Conversation

@clement0010
Copy link
Copy Markdown
Contributor

@clement0010 clement0010 commented May 18, 2026

Issue: #292

Changes

  • Resolve sharded Twingate host at config load time by sending a HEAD request to the JWKS endpoint and extract the Location header
    • resolveTwingateHostname extract the Location header and don't follow the redirect
    • stripNetworkPrefix strips the network prefix from the resolved hostname (e.g., acme.us1.twingate.comus1.twingate.com)
    • Falls back to the original host on any connection error
  • Support sharded hosts in JWT issuer validation via suffix matching in getIssuer()

Note: Sharded host is not supported in production yet. This is preparation work, so the operator is ready when sharding is enabled in production.

@clement0010 clement0010 changed the title feat: resolve sharded Twingate host for JWKS at startup feat: Resolve sharded URL at startup May 18, 2026
@clement0010 clement0010 force-pushed the feat/gateway-multiverse-support branch from c83a34e to d9c0174 Compare May 18, 2026 05:06
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@clement0010 clement0010 force-pushed the feat/gateway-multiverse-support branch from d9c0174 to 0b6bed1 Compare May 18, 2026 05:07
@codecov
Copy link
Copy Markdown

codecov Bot commented May 18, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.40%. Comparing base (789c610) to head (1025fa0).
⚠️ Report is 3 commits behind head on master.
✅ All tests successful. No failed tests found.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #293      +/-   ##
==========================================
+ Coverage   85.24%   85.40%   +0.15%     
==========================================
  Files          36       36              
  Lines        2657     2686      +29     
==========================================
+ Hits         2265     2294      +29     
  Misses        264      264              
  Partials      128      128              
Flag Coverage Δ
integration 53.91% <17.24%> (-0.43%) ⬇️
unit 77.73% <100.00%> (+0.24%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
cmd/start.go 54.54% <100.00%> (+1.42%) ⬆️
internal/config/config.go 88.53% <100.00%> (+1.14%) ⬆️
internal/token/parser.go 93.33% <100.00%> (+1.33%) ⬆️
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@clement0010 clement0010 force-pushed the feat/gateway-multiverse-support branch from 8685a29 to 54a06b0 Compare May 18, 2026 06:12
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR prepares the gateway for sharded Twingate hosts by resolving the effective host during configuration loading and allowing JWT issuer validation for sharded hostnames.

Changes:

  • Adds config-time hostname resolution via HEAD request to the JWKS endpoint.
  • Adds host suffix matching for JWT issuer selection.
  • Adds tests for hostname stripping/resolution and sharded issuer handling.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
internal/config/config.go Adds sharded hostname resolution during config load.
internal/config/config_test.go Adds tests for hostname stripping and redirect resolution.
internal/token/parser.go Adds issuer lookup helper with suffix matching for sharded hosts.
internal/token/parser_test.go Adds tests for sharded issuer lookup and parser behavior.
go.mod Promotes go-retryablehttp to a direct dependency.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/config/config.go Outdated
Comment thread internal/config/config_test.go Outdated
Comment thread internal/config/config.go Outdated
clement0010 and others added 2 commits May 18, 2026 17:30
… test sync

- Skip sharded host resolution when Twingate network is empty
- Replace plain bool with buffered channel for race-safe test assertion
- Add edge case test for empty network in stripNetworkPrefix

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@clement0010 clement0010 requested a review from minhtule May 21, 2026 03:12
@clement0010 clement0010 force-pushed the feat/gateway-multiverse-support branch from a6ffbba to 3edce14 Compare May 22, 2026 07:07
@clement0010 clement0010 force-pushed the feat/gateway-multiverse-support branch from 495f001 to 4e60e31 Compare May 22, 2026 07:25
Comment thread internal/config/config.go Outdated

func (c *Config) ResolveTwingateHost() {
targetURL := fmt.Sprintf("https://%s.%s/api/v1/jwk/ec", c.Twingate.Network, c.Twingate.Host)
resolvedHostname := resolveTwingateHostname(targetURL, c.Twingate.Host, 3)
Copy link
Copy Markdown
Contributor Author

@clement0010 clement0010 May 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this configuration we get:

Step What Duration
Attempt 0 HEAD → timeout 1s
Backoff 0 2^0 × 1s 1s
Attempt 1 HEAD → timeout 1s
Backoff 1 2^1 × 1s 2s
Attempt 2 HEAD → timeout 1s
Backoff 2 2^2 × 1s 4s
Attempt 3 HEAD → timeout 1s

Total = 11s

I think 11s is fine? Alternatively, we can shorten the backoff to 0.5s or set the max attempt to 2, to squeeze within the 10s window

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Yes, I would lower the max retries to 2. Maybe also increase the timeout to 2s.
  • Why is the number of retries passed in as an argument but the 1s timeout is hard coded here?

Copy link
Copy Markdown
Contributor Author

@clement0010 clement0010 May 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the number of retries passed in as an argument but the 1s timeout is hard coded here?

We are setting the same value (1 second) for the test as well, so Go lint complains: internal/config/config.go:196:61: resolveTwingateHostname - timeout always receives 1 * time.Second (1000000000) (unparam), whereas retryMax is set to 0 for all tests

Copy link
Copy Markdown
Contributor

@minhtule minhtule left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix & merge!

Comment thread internal/token/parser.go Outdated
Comment thread internal/config/config.go Outdated

func (c *Config) ResolveTwingateHost() {
targetURL := fmt.Sprintf("https://%s.%s/api/v1/jwk/ec", c.Twingate.Network, c.Twingate.Host)
resolvedHostname := resolveTwingateHostname(targetURL, c.Twingate.Host, 3)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Yes, I would lower the max retries to 2. Maybe also increase the timeout to 2s.
  • Why is the number of retries passed in as an argument but the 1s timeout is hard coded here?

Comment thread internal/config/config_test.go
@minhtule minhtule requested a review from sghiocel May 23, 2026 02:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants