Skip to content

feat: add NonSelectableOutbounds to ConfigResponse#27

Merged
myleshorton merged 5 commits into
mainfrom
fisk/non-selectable-outbounds
Jul 5, 2026
Merged

feat: add NonSelectableOutbounds to ConfigResponse#27
myleshorton merged 5 commits into
mainfrom
fisk/non-selectable-outbounds

Conversation

@myleshorton

@myleshorton myleshorton commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Adds NonSelectableOutbounds []string to ConfigResponse: the tags of outbounds that are infrastructure (e.g. a proxyless download_detour for rule-set fetches) and must not be offered as user-selectable proxies.

The client merges these outbounds into its box config so download_detour / rule references resolve, but excludes them from the auto/manual selector groups. This generalizes the mechanism so the server can introduce new optional/infra outbounds without a client release — the client honors whatever tags appear here.

Consumers (depend on this):

Context: getlantern/engineering#3657.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WGpKfgkbfdJwwsTaouMDoR

Summary by CodeRabbit

  • New Features
    • Added support for a new optional configuration field, non_selectable_outbounds, allowing servers to supply outbound tags that are merged into the client’s sing-box configuration for reference resolution.
    • These tagged outbounds are excluded from both automatic proxy selection and manual proxy selection.
  • Tests
    • Added a unit test to verify non_selectable_outbounds JSON round-trips correctly and is omitted when unset or empty.

Lists outbound tags that are infrastructure (e.g. a proxyless download_detour
for rule-set fetches) and must not be user-selectable. The client merges them
into its box config so references resolve but excludes them from the auto/manual
selector groups — letting the server introduce optional/infra outbounds without
a client release.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGpKfgkbfdJwwsTaouMDoR
Copilot AI review requested due to automatic review settings July 3, 2026 18:26
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9c41a464-7064-49a0-a2dc-ee29fb0ebcc4

📥 Commits

Reviewing files that changed from the base of the PR and between d196786 and 57bc417.

📒 Files selected for processing (2)
  • types.go
  • types_test.go

📝 Walkthrough

Walkthrough

Adds an optional NonSelectableOutbounds field to ConfigResponse and a test that verifies its JSON marshal, unmarshal, and empty-value omission behavior.

Changes

ConfigResponse schema update

Layer / File(s) Summary
Field and round-trip test
types.go, types_test.go
Adds NonSelectableOutbounds []string with the non_selectable_outbounds JSON tag and config-wiring comment, and verifies JSON marshal/unmarshal behavior plus omission when empty.

Estimated code review effort: 1 (Trivial) | ~3 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: adding NonSelectableOutbounds to ConfigResponse.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fisk/non-selectable-outbounds

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds an explicit server-to-client signal for “infrastructure-only” outbounds so clients can include them for resolution (e.g., download_detour / rule references) while excluding them from user-selectable proxy groups.

Changes:

  • Extend ConfigResponse with NonSelectableOutbounds []string serialized as non_selectable_outbounds.
  • Document intended client behavior and compatibility goal (server can introduce new infra outbounds without requiring a client release).

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

Comment thread types.go
@myleshorton

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.

Comment thread types.go
@myleshorton myleshorton requested a review from Copilot July 3, 2026 18:51
@myleshorton

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

…p too

Make explicit that these outbounds are kept out of BOTH selection groups — not
added to the auto/URLTest group (so auto-selection never routes traffic through
them) and not offered in the manual selector — not merely "not user-selectable".

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGpKfgkbfdJwwsTaouMDoR
@myleshorton myleshorton requested a review from Copilot July 5, 2026 17:14
@myleshorton

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comment thread types_test.go
myleshorton added a commit to getlantern/radiance that referenced this pull request Jul 5, 2026
…bounds doc

Spell out that excluded tags are kept out of BOTH the auto (URLTest) group
(auto-selection won't route traffic through them) and the manual selector,
matching the getlantern/common#27 field doc. Comment-only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGpKfgkbfdJwwsTaouMDoR
The round-trip already verifies the value; checking the key name is robust to
encoder formatting.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGpKfgkbfdJwwsTaouMDoR
@myleshorton myleshorton requested a review from Copilot July 5, 2026 17:18
@myleshorton

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comment thread types_test.go Outdated
Back the "empty/nil omitted" claim by marshaling both ConfigResponse{} and
NonSelectableOutbounds: []string{} and asserting the key is absent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGpKfgkbfdJwwsTaouMDoR
@myleshorton myleshorton requested a review from Copilot July 5, 2026 17:25
@myleshorton

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@myleshorton myleshorton merged commit 57aaafd into main Jul 5, 2026
2 checks passed
myleshorton added a commit to getlantern/radiance that referenced this pull request Jul 6, 2026
…ure (#549)

* vpn: route remote rule-set fetches through a fragmenting "mirror" detour

Remote rule-sets (geosite-cn, geosite-ir, geoip-ru, ...) are fetched at
cold start over download_detour:direct, which censors throttle at the SNI
layer. Repoint every direct-fetched remote rule-set to a proxyless "mirror"
outbound — the outline-sdk smart dialer with DoH resolution + TLS-record
fragmentation — so the fetch survives the throttle without a proxy server.
Detour-only (not a selectable proxy); the outbound is injected only when a
rule-set was actually repointed, and its first strategy is plain direct so
uncensored fetches are unaffected.

Region-agnostic: replaces the earlier geosite-cn-only scoping, since IR/RU
rule-sets fetch from the same throttled CDNs (jsDelivr, raw.githubusercontent).

Includes cmd/geosite-throttle-test, the CN-residential harness used to
validate that fragmentation defeats the SNI-keyed throttle (fastly.jsdelivr
+ tlsfrag: full .srs in 3-5s; s3 excluded — its throttle is rate-based).

engineering#3657

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGpKfgkbfdJwwsTaouMDoR

* vpn: update TestBuildOptions_Rulesets for the mirror repoint

buildOptions now repoints direct-fetched remote rule-sets to the mirror
download_detour, so the golden assertions must expect that detour. Add an
expectMirrorDetour helper mirroring repointRuleSetsToMirror and apply it to
the smart-routing and ad-block expected rule-sets.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGpKfgkbfdJwwsTaouMDoR

* vpn: address review — drop ticket refs, guard mirror tag, reviewed harness

- Remove engineering#3657 references from code comments (AGENTS.md: keep
  rationale in git history, not in-code).
- Guard the mirror-outbound append: skip when the merged config already
  defines the "mirror" tag, so it can't produce a duplicate outbound tag.
- Replace the throttle-test harness with its reviewed-final version (TLS-
  wrapped proxy hop so creds aren't sent in cleartext, checked CONNECT write,
  bounded read deadline, non-2xx treated as an error, no external script ref).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGpKfgkbfdJwwsTaouMDoR

* vpn: address review round 2 — tag guard spans endpoints; test reuses repoint

- tagInUse checks both outbounds and endpoints before injecting the mirror
  outbound, since sing-box tags must be unique across both.
- expectMirrorDetour now calls repointRuleSetsToMirror instead of duplicating
  the rewrite predicate, so the test can't drift from production logic.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGpKfgkbfdJwwsTaouMDoR

* vpn: gate the rule-set repoint on the mirror tag being free

Previously repointRuleSetsToMirror ran unconditionally and only the outbound
injection was guarded, so a config already using the "mirror" tag (e.g. on an
endpoint) would leave rule-sets pointing at a foreign tag. Check tagInUse
before repointing (short-circuit), so rule-sets are only rewritten when our
mirror outbound will back the tag. Adds TestTagInUse.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGpKfgkbfdJwwsTaouMDoR

* cmd/geosite-throttle-test: doc OXY_USER contract; CloseRead falls back to Close

- Document that OXY_USER is the bare Oxylabs customer ID that sessUser wraps.
- tlsStreamConn.CloseRead now falls back to a full Close (a *tls.Conn can't
  half-close the read side, so a no-op falsely reports success).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGpKfgkbfdJwwsTaouMDoR

* vpn: cover mirror injection; harness measures wire bytes

- TestBuildOptions_MirrorInjection asserts the mirror outbound is injected and
  rule-sets repointed when the tag is free, and that a pre-existing "mirror"
  tag leaves rule-sets untouched with no duplicate outbound.
- throttle-test fetch() disables transparent gzip and redirect-following so the
  reported byte count reflects the measured URL, not a decoded/redirected body.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGpKfgkbfdJwwsTaouMDoR

* cmd/geosite-throttle-test: reuse one exit per iteration across strategies

Generate the Oxylabs session once per iteration (outside the strategy loop) and
reuse it, so direct #1 and tlsfrag #1 measure the same residential exit — a
controlled per-strategy comparison rather than one confounded by exit variance.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGpKfgkbfdJwwsTaouMDoR

* vpn: rename the "mirror" detour to "proxyless"

The outbound is a single-host fragmenting fetch over the existing Outline
proxyless transport, not a multi-mirror racer — "mirror" overstated it. Rename
the tag, symbols, and files (mirror.go -> proxyless.go) to "proxyless". No
behavior change. True multi-mirror racing, if wanted, is a separate change
(radiance pre-fetching multiple URLs to a local rule-set).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGpKfgkbfdJwwsTaouMDoR

* vpn: make the proxyless rule-set detour server-driven

Move the proxyless outbound out of the client: lantern-cloud now sends the
outline (DoH + TLS-fragmentation) outbound and sets remote rule-sets'
download_detour to it, so its config stays tunable without a client release.

radiance's only job is to not treat that server-sent detour as a selectable
proxy: add "proxyless" to reservedTags and skip reserved tags when collecting
the selectable set in mergeAndCollectTags (infrastructure outbounds are merged
into the config but never offered in the auto/manual groups).

Drops the client-side outbound construction, rule-set repointing, and tag-
collision guard (all now the server's responsibility). Keeps the CN-residential
throttle-test harness.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGpKfgkbfdJwwsTaouMDoR

* vpn: address review — drop code-location ref, fix stale merge doc

- proxyless.go: remove the "(see reservedTags)" code-location reference (AGENTS.md).
- mergeAndCollectTags: doc now reflects that reserved/infrastructure tags are
  merged but omitted from the returned selectable set.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGpKfgkbfdJwwsTaouMDoR

* vpn: generalize — honor server-declared non-selectable outbounds

Instead of hardcoding the "proxyless" tag, the client now excludes whatever the
server lists in ConfigResponse.NonSelectableOutbounds from the selectable proxy
groups (in addition to the base reserved tags). Those outbounds are still merged
into the box config so their references (download_detour) resolve. This lets the
server introduce future optional/infra outbounds with no client release.

- BoxOptions gains NonSelectableOutbounds; populated from the config in backend.
- mergeAndCollectTags excludes reserved ∪ non-selectable tags.
- Drop vpn/proxyless.go (the client no longer knows any specific infra tag).
- Bump getlantern/common for the new field.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGpKfgkbfdJwwsTaouMDoR

* vpn: align mergeAndCollectTags comment with common's NonSelectableOutbounds doc

Spell out that excluded tags are kept out of BOTH the auto (URLTest) group
(auto-selection won't route traffic through them) and the manual selector,
matching the getlantern/common#27 field doc. Comment-only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGpKfgkbfdJwwsTaouMDoR

* vpn: also exclude reserved/non-selectable endpoint tags from the selectable set

mergeAndCollectTags filtered outbound tags but returned endpoint tags unfiltered,
so an infra-tagged endpoint (reserved or server-declared non-selectable) would
have been offered as a selectable proxy. Apply the same filter to endpoints via a
shared skip closure; cover it in the test. Also add a doc comment on the
throttle-test DialStream method.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGpKfgkbfdJwwsTaouMDoR

* go.mod: point getlantern/common at merged main (NonSelectableOutbounds)

common#27 merged; re-point from the branch pseudo-version to merged main.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGpKfgkbfdJwwsTaouMDoR

* vpn: comment wording — non-selectable filter covers endpoints + full reserved set

Follow-up to filtering endpoint tags: the BoxOptions field, mergeAndCollectTags
doc, and test header said "outbounds" though the filter applies to endpoints too;
say "tags". Also list the full reserved set (auto/manual/direct/block) in the
skip comment. Comment-only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGpKfgkbfdJwwsTaouMDoR

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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.

2 participants