Skip to content

feat: add up/set pref Config fields for the daemon#268

Merged
GeiserX merged 1 commit into
mainfrom
feat/up-set-pref-fields
Jun 15, 2026
Merged

feat: add up/set pref Config fields for the daemon#268
GeiserX merged 1 commit into
mainfrom
feat/up-set-pref-fields

Conversation

@GeiserX

@GeiserX GeiserX commented Jun 15, 2026

Copy link
Copy Markdown
Owner

What

Engine ask #21 (the faithfully-wirable subset): 8 Config fields for Go up/set pref flags the daemon can't wire today. Each is behaviorally faithful to Go v1.100.0's tsnet/Prefs layer (researched verbatim) — no inert fields (honest-omission rule).

Advertised to control (wired to existing HostInfo fields)

  • advertise_app_connector: boolHostInfo.AppConnector (Go applyPrefsToHostinfoLocked: hi.AppConnector.Set(advertise)). Go calls .Set unconditionally, so the key is always sent: trueAppConnector:true, false (default)→AppConnector:false (a .Set(false) opt.Bool marshals to false, not omitted — omitting it would be a not-tailscaled fingerprint tell). The app-connector data path (domain routes / 4via6) is a separate deferred subsystem.
  • auto_update_apply: Option<bool>HostInfo.AllowsUpdate (Go: …|| Apply.EqualBool(true)). Some(true)AllowsUpdate:true; None/Some(false)→false (an omitzero bool, omitted — matches Go). No updater is implemented (advertise only).

Store-only (carried prefs — faithful, since Go's engine layer also just carries these; the behavior is daemon/c2n/separate-subsystem/OS-router, documented honestly per field)

auto_update_check, operator_user (daemon LocalAPI uid authz), node_nickname (local profile label), posture_checking (no Hostinfo.PostureChecking exists — posture is a c2n pull the fork doesn't implement; storing == disabled), run_web_client (separate web server), exit_node_allow_lan_access (OS-router route-shaping, inert on the netstack-default fork).

Deliberately NOT added (honest omission — no engine layer to act on them)

The Linux netfilter knobs (--snat-subnet-routes/--stateful-filtering/--netfilter-mode), --unattended (Go ForceDaemon, Windows), and the WIF flags (already behind identity-federation — the daemon enables the feature, no engine code).

Reviewed

Independent wire-parity review caught a HIGH fingerprint bug (the original app_connector omitted the key on false where Go sends AppConnector:false) — fixed (Some(value), mirroring the fork's HostInfo.container Some(false)-is-a-real-signal precedent) + tests updated to assert the Go-faithful wire bytes, empirically confirmed against Go v1.100.0's opt.Bool marshaling.

Gates (local, all green)

cargo test -p geiserx_ts_control -p geiserx_ts_control_serde -p geiserx_tailscale (255 + serde + facade) · clippy -D warnings (3 crates + tun lane) · cargo fmt --check · cargo doc (broken_intra_doc_links=deny) · cargo run -p checks. Tests prove the wire keys via serde_json::to_value, the From<&Config> threading, and fail-closed defaults.

Signed-off-by: Sergio sergio@geiser.cloud

Created using Claude Code (Opus 4.8)

…pdate, operator, nickname, posture, webclient

Add the faithfully-wirable subset of the downstream daemon's engine-ask #21:
eight Go up/set pref flags it cannot wire today, surfaced on tailscale::Config
and threaded to ts_control::Config via From<&Config> like the existing prefs.

Two are genuinely advertised to control (mirroring Go applyPrefsToHostinfoLocked):

- advertise_app_connector (Go Prefs.AppConnector.Advertise) sets
  HostInfo.AppConnector = Some(true) on registration and every map request
  (Go hi.AppConnector.Set(...)). Advertises the capability bool only; the
  app-connector data path (domain routes / 4via6) is a separate subsystem,
  exactly the boundary Go draws.
- auto_update_apply (Go Prefs.AutoUpdate.Apply) feeds HostInfo.AllowsUpdate
  (Go hi.AllowsUpdate = ... || Apply.EqualBool(true)). This fork runs no
  updater; the flag only advertises the node accepts a remote update trigger.

Both ride new MapRequestBuilder methods app_connector()/allows_update(),
mirroring the wire_ingress wiring, and are set in register.rs + client.rs.

Six are store-only carried prefs — faithful because Go's engine layer also just
stores these; the behavior is daemon / c2n / separate-subsystem / OS-router, not
engine. Each is carried on both configs and threaded through From, never read
inside ts_control and never advertised:

- auto_update_check (Go AutoUpdate.Check, local background-checker gate)
- operator_user (Go OperatorUser, daemon LocalAPI uid authz)
- node_nickname (Go ProfileName, local display label)
- posture_checking (Go PostureChecking; no Hostinfo field — posture is a c2n
  pull the fork does not implement, so storing is identical to posture-disabled)
- run_web_client (Go RunWebClient, gates a separate local web-client server)
- exit_node_allow_lan_access (Go ExitNodeAllowLANAccess, OS-router route-shaping
  with no effect on the netstack-default fork that has no host-route layer)

Each store-only field's doc comment honestly states it is a carried pref whose
behavior is daemon/control-side, not an engine action. Per honest-omission, the
Linux netfilter knobs, --unattended, and the WIF flags are deliberately left out
(no engine layer to act on them).

All default off/None (fail-closed). Tests cover From<&Config> threading
(default + set), the AppConnector/AllowsUpdate wire keys via serde_json, and
that a default Config advertises neither.

Signed-off-by: GeiserX <9169332+GeiserX@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@GeiserX, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 11 minutes and 44 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 57f3d837-968e-461d-a570-26b2cc0256c2

📥 Commits

Reviewing files that changed from the base of the PR and between 73f56b1 and 5283dd6.

📒 Files selected for processing (5)
  • src/config.rs
  • ts_control/src/config.rs
  • ts_control/src/map_request_builder.rs
  • ts_control/src/tokio/client.rs
  • ts_control/src/tokio/register.rs
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/up-set-pref-fields

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@GeiserX GeiserX merged commit 8e207af into main Jun 15, 2026
16 checks passed
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.

1 participant