| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
Registry switching, descriptions, and per-registry warnings, without the known-registries list (#3897) * fix(marketplace): switch registries at once, and carry each listed registry's notice Switching registries left the previous registry's packages on screen under the new registry's name until the new fetch landed. The shared component rendered whatever `currentRegistry$` last emitted, and on the brochure that stream only emits once a fetch completes; the OS UI's catalog cache hid the same gap whenever the target registry was not loaded yet. The component now renders only the registry matching the selected url, so a switch shows the cached content or skeletons immediately. The brochure also keeps every registry it has fetched, so switching back is instant and its picker shows a visited registry's live icon instead of the bundled fallback. The Start9 Registry showed the community icon because registry.start9.com served that very image and the picker prefers a registry's live icon. The icon on the server has since been corrected, and the manifest now pins it, so a listed registry that serves a different icon falls back to the pin. The known-registries manifest lists Start9's own four registries, and every entry carries a `warning`: a LocaleString the marketplace shows while that registry is selected, carrying the translations the old per-registry dialog had. An unlisted registry keeps the generic third-party caveat, and the add dialog shows the selected entry's notice rather than a blanket one. A registry that serves no icon no longer counts as drifted from its pin; only a different name or icon does. The KnownRegistry binding must be taken from the Generated Artifacts run. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(registry): declare a description, pinned for listed registries A registry can now describe itself: `start-registry info set-description` stores markdown (a LocaleString, so it can carry translations) in the index, and `info` returns it. The marketplace shows it in an info banner above every other notice while that registry is selected, rendered through the same markdown pipeline as release notes. The known-registries manifest pins a description for each listed registry the way it pins the name and icon: the pinned text is what the marketplace shows, and a listed registry that serves a different one trips the drift banner. The four Start9 registries get their descriptions here; the same texts are to be set on the registries themselves. The RegistryInfo, KnownRegistry, FullIndex, and SetDescriptionParams bindings and the start-registry man pages must be taken from the Generated Artifacts run. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs(marketplace): send beta testers to the service-testing room Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs(marketplace): point packagers at the service-packaging room, not the submissions inbox Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs(marketplace): say when a service belongs in a dedicated registry instead Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs(marketplace): take the edited descriptions, and tell beta testers bugs are expected Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs(marketplace): translate the pinned registry descriptions Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs(marketplace): pin the icons the Start9 and Beta registries now serve Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * chore(shared): bundle the icons the Start9 and Beta registries serve as their fallbacks Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(marketplace): display verified registries from the manifest alone The manifest is now the only authored identity for a registry Start9 lists. It moves into @start9labs/shared, which bundles it as the fallback for when the published copy can't be fetched, so the hardcoded defaultIdentities, the knownRegistries URL list, and the four bundled registry icons go away. The brochure still serves it from .well-known through its assets entry, and a push to master still redeploys it. One resolver replaces resolveIdentity, resolveIcon, pinnedIcon, findKnown, and identityMatches. A listed registry shows its listed name, icon, description, and warning, whatever its server reports; an unlisted one shows what it reports, except that a name containing a listed name or "Start9" is replaced by the registry's host, and the page says so. The drift banner goes with the comparisons behind it: a listed registry can't disagree with its listing on screen, and impersonation is caught by the name rule, which a lookalike icon or description never was. Listed registries carry a "Verified by Start9" mark in the picker, and the generic notice for unlisted ones now says what it can stand behind: the registry is not on the list Start9 publishes. The OS side compares a fetched name against the stored one before writing it back, instead of against the listed one. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs(start-sdk): how a registry gets verified by Start9 A new Verification page in the hosting chapter says what a listing attests to (the address and an operator Start9 can reach, not the services), the requirements, how to apply, and how a listing is kept current. The StartOS book and the publishing page point at it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(marketplace): name a listed registry's operator and contact Every listing now carries the operator's public name and a contact email, shown beneath the description while the registry is selected. Start9's own entries name Start9 and leave the contact to be filled in. The verification page asks for both, routes applications through the submissions inbox like a package, and says that an unlisted registry, Tor-only included, needs none of this. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(marketplace): lay the info banner out as Description and Contact Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(marketplace): drop the known-registries list Adding a registry is the URL prompt again, a registry's name and icon are what it serves, and the caveat banner is keyed by URL, as before #3865. The manifest, the marketplace.known-registries RPC and its binding, the add dialog's list, the pinned identities, the verified mark, and the verification policy page all go. The switch fix, the description feature, and the per-registry warning texts stay, with the beta texts saying bugs are expected and the community beta text carrying both caveats. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * chore(start-registry): 1.1.0, since a registry can now declare a description Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(marketplace): no caveat banner on the Community Registry Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * chore(registry): regenerate bindings and man pages Helix-Harness: pi Helix-Model: openai-codex/gpt-5.6-sol --------- Co-authored-by: Matt Hill <9935159+MattDHill@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: Helix <267227783+helix-nine@users.noreply.github.com> | 3 天前 | |
Registry switching, descriptions, and per-registry warnings, without the known-registries list (#3897) * fix(marketplace): switch registries at once, and carry each listed registry's notice Switching registries left the previous registry's packages on screen under the new registry's name until the new fetch landed. The shared component rendered whatever `currentRegistry$` last emitted, and on the brochure that stream only emits once a fetch completes; the OS UI's catalog cache hid the same gap whenever the target registry was not loaded yet. The component now renders only the registry matching the selected url, so a switch shows the cached content or skeletons immediately. The brochure also keeps every registry it has fetched, so switching back is instant and its picker shows a visited registry's live icon instead of the bundled fallback. The Start9 Registry showed the community icon because registry.start9.com served that very image and the picker prefers a registry's live icon. The icon on the server has since been corrected, and the manifest now pins it, so a listed registry that serves a different icon falls back to the pin. The known-registries manifest lists Start9's own four registries, and every entry carries a `warning`: a LocaleString the marketplace shows while that registry is selected, carrying the translations the old per-registry dialog had. An unlisted registry keeps the generic third-party caveat, and the add dialog shows the selected entry's notice rather than a blanket one. A registry that serves no icon no longer counts as drifted from its pin; only a different name or icon does. The KnownRegistry binding must be taken from the Generated Artifacts run. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(registry): declare a description, pinned for listed registries A registry can now describe itself: `start-registry info set-description` stores markdown (a LocaleString, so it can carry translations) in the index, and `info` returns it. The marketplace shows it in an info banner above every other notice while that registry is selected, rendered through the same markdown pipeline as release notes. The known-registries manifest pins a description for each listed registry the way it pins the name and icon: the pinned text is what the marketplace shows, and a listed registry that serves a different one trips the drift banner. The four Start9 registries get their descriptions here; the same texts are to be set on the registries themselves. The RegistryInfo, KnownRegistry, FullIndex, and SetDescriptionParams bindings and the start-registry man pages must be taken from the Generated Artifacts run. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs(marketplace): send beta testers to the service-testing room Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs(marketplace): point packagers at the service-packaging room, not the submissions inbox Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs(marketplace): say when a service belongs in a dedicated registry instead Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs(marketplace): take the edited descriptions, and tell beta testers bugs are expected Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs(marketplace): translate the pinned registry descriptions Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs(marketplace): pin the icons the Start9 and Beta registries now serve Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * chore(shared): bundle the icons the Start9 and Beta registries serve as their fallbacks Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(marketplace): display verified registries from the manifest alone The manifest is now the only authored identity for a registry Start9 lists. It moves into @start9labs/shared, which bundles it as the fallback for when the published copy can't be fetched, so the hardcoded defaultIdentities, the knownRegistries URL list, and the four bundled registry icons go away. The brochure still serves it from .well-known through its assets entry, and a push to master still redeploys it. One resolver replaces resolveIdentity, resolveIcon, pinnedIcon, findKnown, and identityMatches. A listed registry shows its listed name, icon, description, and warning, whatever its server reports; an unlisted one shows what it reports, except that a name containing a listed name or "Start9" is replaced by the registry's host, and the page says so. The drift banner goes with the comparisons behind it: a listed registry can't disagree with its listing on screen, and impersonation is caught by the name rule, which a lookalike icon or description never was. Listed registries carry a "Verified by Start9" mark in the picker, and the generic notice for unlisted ones now says what it can stand behind: the registry is not on the list Start9 publishes. The OS side compares a fetched name against the stored one before writing it back, instead of against the listed one. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs(start-sdk): how a registry gets verified by Start9 A new Verification page in the hosting chapter says what a listing attests to (the address and an operator Start9 can reach, not the services), the requirements, how to apply, and how a listing is kept current. The StartOS book and the publishing page point at it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(marketplace): name a listed registry's operator and contact Every listing now carries the operator's public name and a contact email, shown beneath the description while the registry is selected. Start9's own entries name Start9 and leave the contact to be filled in. The verification page asks for both, routes applications through the submissions inbox like a package, and says that an unlisted registry, Tor-only included, needs none of this. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(marketplace): lay the info banner out as Description and Contact Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(marketplace): drop the known-registries list Adding a registry is the URL prompt again, a registry's name and icon are what it serves, and the caveat banner is keyed by URL, as before #3865. The manifest, the marketplace.known-registries RPC and its binding, the add dialog's list, the pinned identities, the verified mark, and the verification policy page all go. The switch fix, the description feature, and the per-registry warning texts stay, with the beta texts saying bugs are expected and the community beta text carrying both caveats. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * chore(start-registry): 1.1.0, since a registry can now declare a description Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(marketplace): no caveat banner on the Community Registry Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * chore(registry): regenerate bindings and man pages Helix-Harness: pi Helix-Model: openai-codex/gpt-5.6-sol --------- Co-authored-by: Matt Hill <9935159+MattDHill@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: Helix <267227783+helix-nine@users.noreply.github.com> | 3 天前 | |
docs: backport live-docs a730a73a2 to master Backports the following from live-docs: - a730a73a2 docs: correct the Bitcoin flavors table for the Knots registry move (#3895) | 4 天前 | |
chore(start-sdk): promote next release to 3.0.0 (#3900) Helix-Harness: pi Helix-Model: openai-codex/gpt-5.6-sol | 3 天前 | |
Registry switching, descriptions, and per-registry warnings, without the known-registries list (#3897) * fix(marketplace): switch registries at once, and carry each listed registry's notice Switching registries left the previous registry's packages on screen under the new registry's name until the new fetch landed. The shared component rendered whatever `currentRegistry$` last emitted, and on the brochure that stream only emits once a fetch completes; the OS UI's catalog cache hid the same gap whenever the target registry was not loaded yet. The component now renders only the registry matching the selected url, so a switch shows the cached content or skeletons immediately. The brochure also keeps every registry it has fetched, so switching back is instant and its picker shows a visited registry's live icon instead of the bundled fallback. The Start9 Registry showed the community icon because registry.start9.com served that very image and the picker prefers a registry's live icon. The icon on the server has since been corrected, and the manifest now pins it, so a listed registry that serves a different icon falls back to the pin. The known-registries manifest lists Start9's own four registries, and every entry carries a `warning`: a LocaleString the marketplace shows while that registry is selected, carrying the translations the old per-registry dialog had. An unlisted registry keeps the generic third-party caveat, and the add dialog shows the selected entry's notice rather than a blanket one. A registry that serves no icon no longer counts as drifted from its pin; only a different name or icon does. The KnownRegistry binding must be taken from the Generated Artifacts run. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(registry): declare a description, pinned for listed registries A registry can now describe itself: `start-registry info set-description` stores markdown (a LocaleString, so it can carry translations) in the index, and `info` returns it. The marketplace shows it in an info banner above every other notice while that registry is selected, rendered through the same markdown pipeline as release notes. The known-registries manifest pins a description for each listed registry the way it pins the name and icon: the pinned text is what the marketplace shows, and a listed registry that serves a different one trips the drift banner. The four Start9 registries get their descriptions here; the same texts are to be set on the registries themselves. The RegistryInfo, KnownRegistry, FullIndex, and SetDescriptionParams bindings and the start-registry man pages must be taken from the Generated Artifacts run. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs(marketplace): send beta testers to the service-testing room Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs(marketplace): point packagers at the service-packaging room, not the submissions inbox Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs(marketplace): say when a service belongs in a dedicated registry instead Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs(marketplace): take the edited descriptions, and tell beta testers bugs are expected Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs(marketplace): translate the pinned registry descriptions Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs(marketplace): pin the icons the Start9 and Beta registries now serve Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * chore(shared): bundle the icons the Start9 and Beta registries serve as their fallbacks Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(marketplace): display verified registries from the manifest alone The manifest is now the only authored identity for a registry Start9 lists. It moves into @start9labs/shared, which bundles it as the fallback for when the published copy can't be fetched, so the hardcoded defaultIdentities, the knownRegistries URL list, and the four bundled registry icons go away. The brochure still serves it from .well-known through its assets entry, and a push to master still redeploys it. One resolver replaces resolveIdentity, resolveIcon, pinnedIcon, findKnown, and identityMatches. A listed registry shows its listed name, icon, description, and warning, whatever its server reports; an unlisted one shows what it reports, except that a name containing a listed name or "Start9" is replaced by the registry's host, and the page says so. The drift banner goes with the comparisons behind it: a listed registry can't disagree with its listing on screen, and impersonation is caught by the name rule, which a lookalike icon or description never was. Listed registries carry a "Verified by Start9" mark in the picker, and the generic notice for unlisted ones now says what it can stand behind: the registry is not on the list Start9 publishes. The OS side compares a fetched name against the stored one before writing it back, instead of against the listed one. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * docs(start-sdk): how a registry gets verified by Start9 A new Verification page in the hosting chapter says what a listing attests to (the address and an operator Start9 can reach, not the services), the requirements, how to apply, and how a listing is kept current. The StartOS book and the publishing page point at it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(marketplace): name a listed registry's operator and contact Every listing now carries the operator's public name and a contact email, shown beneath the description while the registry is selected. Start9's own entries name Start9 and leave the contact to be filled in. The verification page asks for both, routes applications through the submissions inbox like a package, and says that an unlisted registry, Tor-only included, needs none of this. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(marketplace): lay the info banner out as Description and Contact Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(marketplace): drop the known-registries list Adding a registry is the URL prompt again, a registry's name and icon are what it serves, and the caveat banner is keyed by URL, as before #3865. The manifest, the marketplace.known-registries RPC and its binding, the add dialog's list, the pinned identities, the verified mark, and the verification policy page all go. The switch fix, the description feature, and the per-registry warning texts stay, with the beta texts saying bugs are expected and the community beta text carrying both caveats. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * chore(start-registry): 1.1.0, since a registry can now declare a description Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(marketplace): no caveat banner on the Community Registry Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * chore(registry): regenerate bindings and man pages Helix-Harness: pi Helix-Model: openai-codex/gpt-5.6-sol --------- Co-authored-by: Matt Hill <9935159+MattDHill@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: Helix <267227783+helix-nine@users.noreply.github.com> | 3 天前 | |
chore(start-sdk): promote next release to 3.0.0 (#3900) Helix-Harness: pi Helix-Model: openai-codex/gpt-5.6-sol | 3 天前 | |
| 4 天前 | ||
fix(start-wrt): hairpin published ports for every profile with Internet or LAN access (#3888) * fix(start-wrt): scope published-port NAT reflection to permitted zones Published-port redirects never set `reflection_zone`, so fw4 defaulted it to the redirect's `dest` zone and emitted hairpin rules matching only the target device's own subnet. A client on a different Security Profile — one already permitted to forward into the target's zone — hit the router's INPUT chain instead of the DNAT when it used the router's WAN address. Set `reflection_zone` to the target's zone plus every zone a `config forwarding` section already permits to forward into it. Not every zone: each reflection zone becomes the reflection redirect's `src`, which sets fw4's `dflags.dnat` and emits a blanket `ct status dnat accept` into that zone's forward chain ahead of the zone policy, so a reflected flow cannot be rejected afterwards. Also set `reflection '0'` on any port carrying a source restriction. fw4 builds the reflection redirect fresh and copies neither `src_ip` nor `src_mac` (`fw4.uc` ~2898), taking its saddr from the reflection zone's subnets — so a source-restricted forward was reachable by hairpin from any client in the reflection zone, including the same-subnet case that already worked before this change. * refactor(start-wrt): type FirewallRedirect.reflection as Option<bool> `FirewallZone.masq`/`masq6`/`mtu_fix` are `Option<bool>` and serialize as '1'/'0'; `reflection` is the same kind of fw4 boolean in the same struct family, so give it the same type instead of the older `Option<String>` idiom. The bytes written are unchanged. * fix(start-wrt): emit reflection zones only for existing, non-masq zones `reflection_zones` copied `config forwarding` src names verbatim, excluding only the literal "wan". fw4 parses each `reflection_zone` entry as a zone reference: one name that is not a parsed zone fails the whole option and drops the entire redirect — WAN-side DNAT included — and a `src '*'` forwarding (legal fw4) would crash ruleset rendering outright when fw4 dereferences the wildcard's subnets. Stock images never write such forwardings, but an adopted config can carry them, and a zone deleted after the list was written turns into exactly that invalid name. Filter the list against the `config zone` sections actually present, and exclude WAN-like zones by property (`masq` set) rather than by the name "wan"; the redirect's own src zone stays excluded as before. Also write down why cross-zone hairpin is DNAT-only: the reflection SNAT lands in the emitted zone's srcnat chain, which a hairpinned flow never enters, so the server sees the client's real address and replies via the router — intentional, not a missing rule. * fix(start-wrt): resync reflection zones on profile changes and at boot `reflection_zone` was written only by `published-ports set`, but it is derived from the `config forwarding` set, which changes underneath it: - Deleting a profile removed its zone but left the name in every list carrying it, and fw4 treats one unknown name as an invalid option — dropping the whole redirect, WAN-side DNAT included. Deleting a guest profile could silently kill an admin-LAN published port from the Internet until some unrelated re-save rewrote the pp_ sections. - Revoking a profile's Access left it in the lists, so its clients kept reaching the port via the WAN address — the exact reflect-then-reject hole the scoped list exists to prevent, with no rule able to stop it. - A profile granted Access later (or created with it) was missing from the lists, so its clients kept hitting the original bug. Add `sync_reflection_zones`, one pass re-deriving the list on every redirect tagged `_pp_id` or `_apf_label` (skipping `reflection '0'`), and run it wherever the inputs or the redirects change: at the end of `rewrite_firewall` and `delete_config` (both already restart the firewall afterwards), in `published-ports set` (replacing the inline computation), in `apply_forward` — automatic UPnP/PCP forwards now get the same scoped hairpin instead of fw4's dest-zone default, closing the canonical StartOS case of a phone on another profile reaching a UPnP-opened port by the server's public hostname — and once at boot, so routers already carrying stale lists heal (reloading the firewall only when the pass changed something). * fix(start-wrt): resolve a published device's zone by address, not guess `dest_zone` came from the neighbor table alone and fell back to "lan" whenever the device had no entry — and a device that is offline (or whose entry aged out) still passes validation, because its IPv4 resolves from the static reservation `set` itself creates. Since every save rewrites all rules, toggling any port while one target was offline silently rewrote that target's rule with the guessed zone; with scoped reflection the wrong guess also computes the wrong permitted set, e.g. handing an IoT profile with Access to the admin LAN a hairpin route to a server that actually lives in a guest zone. Resolve the zone from the device's IPv4 against the interface subnets of the non-masquerading firewall zones (every profile is a /24 at its gateway; the static reservation covers an offline device, and configs-only mode too), keeping the neighbor-table result as a logged cross-check only. A device that still cannot be placed keeps `dest 'lan'` — the WAN-side DNAT stays as it was — but gets `reflection '0'`: never hairpin into a guessed zone, and never refuse the save over one offline device. `set` now parses "network" alongside "firewall"/"dhcp"; the file is round-tripped unchanged. * docs(start-wrt): true up the hairpin section's wording and scope - "every other profile that profile permits to reach it" read as though the target's profile grants the access; the Access setting lives on the *other* profile (see security-profiles.md), so say so. - "answered by the router itself" becomes "the router answers instead of the device" — plainer. - Reflection matches only addresses actually on the WAN interface: behind CGNAT or another upstream router the public IP a domain resolves to never hairpins, so one clause now says so before a support thread has to. - Automatic (UPnP/PCP) forwards are scoped identically now, so the section says they are covered. * fix(start-wrt): fall back to the neighbor table for a device's zone Resolving the zone by address alone dropped the neighbor-table result that the previous code relied on, so a device with no IPv4 at all — an IPv6-only target, which has neither a lease nor a reservation to place it — fell to the "lan" guess even while the neighbor table knew its zone. The IPv6 forward rule's `dest` then named the wrong zone and fw4 bound it to the wrong egress interfaces. The same applied to an IPv4 outside every profile subnet. Pull the resolution into `device_zone`: the address decides when it resolves (an offline device with a reservation still places, and a save while one target is offline no longer rewrites its rule around a guess), and the neighbor table places what the address cannot. Only a device neither can place keeps the "lan" guess, and only that one is never hairpinned. Tests run non-effectful, so the helper is what makes the fallback testable. Claude-Session: https://claude.ai/code/session_01Mx4wjVx7EC29aBsQmSt5xv * fix(start-wrt): clear reflection zones on reflection-off redirects `sync_reflection_zones` left `reflection '0'` redirects untouched on the grounds that they carry no list. fw4 validates every `reflection_zone` name before it reads `reflection` or `enabled`, so a stale name on such a redirect still drops the whole section, WAN-side DNAT included. Our writer never emits that combination, but a hand-edited config can, and the boot heal exists precisely for configs we did not write. Empty the list on those redirects instead of skipping them; a redirect that is already clean still counts as unchanged. Claude-Session: https://claude.ai/code/session_01Mx4wjVx7EC29aBsQmSt5xv * fix(start-wrt): hairpin published ports for every profile with Internet or LAN access A published port is a public resource, so honoring profile isolation on the router's public address only blocked the one path the operator had already opened to the world (review on #3888). The hairpin now serves every zone that qualifies on either ground: Internet access — a forwarding into a masquerading zone with no unqualified REJECT toward it, which excludes WAN Access None, Whitelist's catch-all, and an active blackout window — or Access to the target's profile, which keeps the original fix for a permitted profile that has no Internet. Listing a zone in `reflection_zone` is not enough on its own: fw4 sets the `dnat` flag that emits `ct status dnat accept` only on the redirect's src and dest zones (fw4.uc:2748), never on the reflection zones, so a reflected flow from an isolated zone reached that zone's forward chain and was rejected by policy. The previous comment on `reflection_zones` claimed otherwise; the bench never exercised it because every zone benched had a forwarding. The daemon now installs one chain-pre include for fw4's top-level `forward` chain — `ct status dnat accept` — at boot, so binary-updated routers converge without a reflash. It matches only flows a DNAT rule rewrote, and the only DNAT into the LAN is a published port or an automatic forward. IPv6 has no DNAT, so each unrestricted `pp_*_v6` WAN rule is copied once per hairpin zone with only `src` changed, tagged `_pp_hairpin`, and rebuilt wholesale on every sync rather than tracked incrementally. `list` ignores the copies (src != wan); `reconcile` retargets them and `remove_ports_for_macs` drops them with the rule through `is_pp_v6_rule`. The WAN-schedule crontab rewrites the firewall behind the daemon's back, so both cron edges now run `startwrt-cli published-ports sync-hairpin` between `uci commit` and the reload; `evaluate_and_apply_schedules` syncs in-process. Claude-Session: https://claude.ai/code/session_01Ctt2FvqXEcPCJCtmGayHee * fix(start-wrt): read WAN Whitelist and Blacklist entries against the published address The Internet ground of the hairpin looked only at the shape of a profile's rules toward its egress zone: an unqualified REJECT cancelled the forwarding, anything else left it standing. That read a Whitelist as never having Internet access and a Blacklist as always having it, whatever their entries named — so a Whitelist holding the router's public address was denied the hairpin (closed against the operator's intent) and a Blacklist blocking that address was granted it (open against it). `hairpin_zones` now walks the zone's rules toward the egress zone in config order, as fw4 does ahead of the forwarding, and takes the first rule whose destination covers one of the port's public addresses: ACCEPT grants, REJECT or DROP cancels, no covering rule leaves the forwarding to decide. A rule narrowed by anything but a destination address, or with a destination that does not parse, covers nothing — the same strict reading unknown shapes had before. The blackout and catch-all cases fall out of the same walk, so `is_unqualified_reject` is gone. An IPv4 redirect is public at the router's WAN IPv4 addresses, which are not in the config: `sync_hairpin` takes them as an argument, callers obtain them through the new `CtrlContext::wan_ipv4_addrs` (ubus, empty when not effectful or unknown), and the two context-less daemon paths call `system::wan_ipv4_addrs` directly. An unknown address degrades to the previous behavior. An IPv6 rule is public at its own `dest_ip`, so its copies need no live input. Because the lists now depend on the WAN address, the `wan` hotplug hook runs `sync-hairpin` and a reload on ifup/ifupdate; the boot heal already covers restarts. Claude-Session: https://claude.ai/code/session_01EqVkyd1zVQc54m4S6HrHJV | 4 天前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 3 天前 | ||
| 3 天前 | ||
| 4 天前 | ||
| 3 天前 | ||
| 3 天前 | ||
| 3 天前 | ||
| 4 天前 | ||
| 4 天前 |