Skip to content

mihomo: live delivery of nodes and rules via providers#119

Open
Postlog wants to merge 1 commit into
mainfrom
feat/live-providers
Open

mihomo: live delivery of nodes and rules via providers#119
Postlog wants to merge 1 commit into
mainfrom
feat/live-providers

Conversation

@Postlog

@Postlog Postlog commented Jun 24, 2026

Copy link
Copy Markdown
Owner

Why

A mihomo subscription was one YAML doc — nodes (proxies:), groups and rules all inlined — so whether an operator edit reaches a client depends on the app re-pulling the whole profile. Reading the ClashMi (KaringX/clashmi) source confirmed mobile has no background profile-update timer (the periodic timer is gated on isPC(); no workmanager/BGTask): the profile refreshes only on app foreground / tunnel (re)connect. So Profile-Update-Interval cannot guarantee "edit the config → connected clients apply it within an hour" on phones.

mihomo's proxy-providers/rule-providers carry an interval the core itself re-fetches while the tunnel is up, independent of the app UI. This PR delivers nodes and authored rule lists that way.

What

  • render: drop inline proxies:; emit a proxy-providers: pointing at /sub/mihomo/{token}/proxies (per-config proxies_interval); proxy-groups reference it via use:+filter: (anchored, regexp.QuoteMeta-escaped — labels may contain emoji/regex metachars); authored rule-providers point at /sub/mihomo/{token}/rules/{name}. New RenderProxiesPayload / RenderAuthoredProvider.
  • mihomo: RuleProvider.Source (external|authored) + a target-less matcher tree (reuses RoutingRule, Target==nil; leaf + logical AND/OR/NOT, never MATCH/RULE-SET/SUB-RULE — mihomo forbids those in a classical provider); Profile.ProxiesInterval; decode/validate + sentinels.
  • migration 0005 (additive, no rebuild): rule_providers.source, recursive mihomo_authored_matchers, mihomo_profile.proxies_interval.
  • repository: save/read/clone authored matchers; source column.
  • ogen: subProxies/subRules endpoints reusing the existing HMAC token resolution; MihomoProvider.source+matchers, MihomoConfig.proxiesInterval, schema sources.
  • admin UI: rule-provider source toggle + authored-list editor (reuses rule-node, RULE-SET filtered out) + hints; new "nodes update interval" field.

subgen stays MIT and never imports mihomo / decodes mrs: external mrs is referenced as its own provider; composing "authored + external" toward one target is two RULE-SET skeleton lines (routing-identical). Design + trade-offs: the archived OpenSpec change openspec/changes/archive/2026-06-30-live-providers/ (design.md).

Scope boundary

The skeleton rules: and the rule-providers:/proxy-providers: declarations stay in the base profile; only the content each provider fetches is live. Structural changes (new groups/targets, reordering, adding a whole provider) still need a profile reload.

Documentation

Rebased onto current main. Following the OpenSpec migration (#121), this PR ships its design as an OpenSpec change (archived under openspec/changes/archive/2026-06-30-live-providers/, the former ADR-0010) and applies the spec deltas to the living specs (subscription-delivery, mihomo-rendering, mihomo-config). openspec validate --specs --strict is green; the CHANGELOG entry links the archived change.

Testing

make all is green (generate + lint + unit -race + integration + apitest against real 3x-ui panels in docker):

  • The mihomo decode/validate/render logic is unit-tested (authored matcher tree, use:+filter: groups, classical-text rendering, source validation).
  • apitest updated for the new wire contract: the node list is asserted via GET /sub/{kind}/{token}/proxies (no longer inline in /sub); the config schema advertises the new proxy-providers generated key; the black-box save struct carries source/proxiesInterval.
  • Hardened the shared-panel inbound seeding to be genuinely idempotent (the parallel panel-gated suites raced to create the same inbound).

🤖 Generated with Claude Code

Deliver the node list as a proxy-provider and authored rule lists as
rule-providers bound to the subscription token, so the mihomo core refreshes
them on their interval while connected — operator edits reach a connected
client without a profile reload (mobile apps have no background profile timer;
verified against ClashMi source).

- render: drop inline proxies:; emit proxy-providers pointing at
  /sub/mihomo/{token}/proxies (per-config proxies_interval); proxy-groups
  reference it via use:+filter (QuoteMeta-escaped); authored rule-providers
  point at /sub/mihomo/{token}/rules/{name}; new RenderProxiesPayload /
  RenderAuthoredProvider.
- mihomo: RuleProvider.Source (external|authored) + authored matcher tree
  (RoutingRule, Target nil); Profile.ProxiesInterval; decode/validate +
  sentinels.
- migration 0005 (additive): rule_providers.source, mihomo_authored_matchers
  (recursive), mihomo_profile.proxies_interval.
- repository: save/read/clone authored matchers; source column.
- ogen: subProxies/subRules endpoints reusing the HMAC token resolution;
  MihomoProvider.source+matchers, MihomoConfig.proxiesInterval, schema sources.
- admin UI: provider source toggle + authored-list editor (reused rule-node,
  RULE-SET filtered out); nodes-update-interval field.

See the archived OpenSpec change
openspec/changes/archive/2026-06-30-live-providers/.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@Postlog Postlog force-pushed the feat/live-providers branch from a2c1057 to f554591 Compare June 30, 2026 17:40
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