Releases: daeuniverse/honk
Release list
v0.0.1.beta.83
Highlights
- VLESS Vision now pads uploads and supports independent uplink/downlink Direct transitions. Direct eligibility follows observed inner-TLS records, with early data and undecided inspection expiry retaining the selected outer transport.
- Eligible long-lived, unencrypted TLS/REALITY Vision connections can hand over to kernel
splice(2)after both directions are Direct and 256 KiB has moved. Pre-staging capability denial falls back to the same copy stream; VLESS Encryption keeps its outer transport and header-XOR layer. - Routing now treats ordinary domain and geosite entries as alternatives, while Clash Direct mode stays consistent between userspace and kernel hooks.
- Quoted DNS upstream names, rule targets and fallbacks are normalized consistently. The startup guide now lists the required nftables conntrack and queue kernel options.
What's Changed
New Features
- Add Vision uplink padding, eligible uplink Direct, and safe long-flow splice handover; bound random-mode Encryption Direct to 8 KiB copied/acknowledged chunks while preserving wire state — by @Glassyiris in 7ef8a339 (#304).
Bug Fixes
- Normalize Clash Direct mode consistently across every routing hook — by @Glassyiris in 8ddcbb2e (#300).
- OR geosite matches with ordinary domain alternatives, backed by shared parsed-rule goldens for userspace and real-kernel checks — by @Glassyiris in a5ddcef7 (#303).
- Compare DNS upstream names, request/response targets and fallbacks without their syntactic quotes — by @Zakkaus in a064fec3 (#305).
Documentation
- List
CONFIG_NFT_CTandCONFIG_NFT_QUEUEalongside the required netfilter queue options — by @Zakkaus in d6115a2b (#306). - Refresh CI's install-action dependency to 2.87.20 — by @dependabot[bot] in 1db6e584 (#301).
Full Changelog: v0.0.1.beta.82...v0.0.1.beta.83
v0.0.1.beta.82
Highlights
- Use marks to choose a WAN: send honk's own connections through one uplink and selected direct traffic through another.
- Score shares a bounded node pool across routes and limits extra trial traffic.
- Fix eBPF verifier issues with complex routing rules.
What's Changed
New Features
- Support global socket marks and direct-rule marks by @Glassyiris in 5efb8c5.
- Simplify Score comparisons and trial scheduling by @Glassyiris in 2ce0f5c.
Bug Fixes
- Fix routing verifier issues by @Zakkaus in e400c04.
- Limit Score trials by business traffic and budget by @Glassyiris in ac457cf.
Performance
- Share bounded Score node pools across routes by @Glassyiris in 5c01a48.
Dual-WAN setup
This example sends honk's own connections and default direct traffic through WAN1, and direct traffic to example.net through WAN2.
Merge it into your existing configuration without removing local-network or management-access rules. The example's fallback is direct; keep your existing proxy fallback if you use one.
global {
so_mark_from_dae: 0x200
}
routing {
domain(suffix: example.net) -> direct(mark: 0x300)
fallback: direct(mark: 0x200)
}First, configure unused routing tables 1001 and 1002 for WAN1 and WAN2. Include local-network routes, each WAN's connected network and default route. Set up forwarding, firewall rules and IPv4 SNAT as needed. Then run as root:
ip -4 rule add pref 10000 fwmark 0x200/0x3fffffff lookup 1001
ip -4 rule add pref 10001 fwmark 0x300/0x3fffffff lookup 1002For IPv6, configure the corresponding IPv6 routing tables and make sure each uplink accepts the client's source prefix before adding:
ip -6 rule add pref 10000 fwmark 0x200/0x3fffffff lookup 1001
ip -6 rule add pref 10001 fwmark 0x300/0x3fffffff lookup 1002Keep these points in mind:
- Do not omit the mask. honk adds internal flags, so
0x300may appear as0x40000300. - Restart after changing the global mark. Direct rules can be reloaded. A nonzero direct-rule mark replaces the global mark; it is not combined with it.
- Proxy groups cannot have separate marks. Proxy connections, DNS upstreams and health checks still use the global mark. This feature does not provide automatic load balancing or failover.
- To intercept host traffic on both WANs, list both real interfaces in
wan_interface;autodoes not mean all uplinks. ipcommands only change runtime state. On VyOS, persist the setup and update routes when DHCP/PPPoE gateways change. Avoid duplicate rules. honk does not manage these routes or NAT for you.
Verification
CI and all eight release builds passed. A one-hour VyOS observation recorded no service restarts or NFQUEUE errors; the maintainer accepted the observed node-side timeouts. The example passed configuration validation and isolated route-lookup checks. No live dual-ISP test was performed, and production WAN settings were not changed.
Full Changelog: v0.0.1.beta.81...v0.0.1.beta.82
Debug
Rolling debug build; replaced by subsequent successful debug-tag runs.
Source tag: debug.2026.9.24.1
Commit: 03a1529de89e57c792c0cc610dd96342c6fe2feb
Build: https://github.com/daeuniverse/honk/actions/runs/36001012281
v0.0.1.beta.81
Highlights
- Score now preserves active incumbents and schedules bounded validation for carrier pressure; client-side TCP disconnects settle neutrally instead of counting as upstream failures.
- Tighten live UDP evidence, transport-error boundaries, DNS retry classification, and native routing fact handling.
- Release archives now have accompanying
.dgstchecksum files; a systemd unit is available in the repository underinstall/honk.service.
What's Changed
New Features
- Publish .dgst release checksums and add a repository systemd unit (#285) by @Zakkaus in 697ac9e
- Preserve active incumbents and validate carrier pressure (#279) by @Glassyiris in 318edf5
Bug Fixes
- Keep client-side TCP disconnects neutral (#284) by @Glassyiris in 1764bd5
- Complete live UDP evidence and policy follow-ups (#282) by @Glassyiris in 30a363b
- Copy fact bitmaps into stack areas instead of keeping map pointers (#281) by @Zakkaus in a421f9d
- Classify transport failures before retrying, reusing or replacing (#278) by @Zakkaus in d464b8e
- Surface gRPC and VMess rejections as stream errors (#277) by @Zakkaus in ca54df7
- Keep the injected direct and block out of group membership (#275) by @Zakkaus in dfe4638
- Stamp idle age at stream transitions instead of sampling it (#273) by @Zakkaus in 1b6e178
- Keep packet-local SS2022 and XUDP failures off the shared transport (#272) by @Zakkaus in 3d94f08
Performance
Contributors
Thanks to @xz-dev for her contributions to this release.
Validation and known limits
Branch CI passed for 697ac9ea. Its runtime code is unchanged from 1764bd58, which completed a two-hour real-eBPF/NFQUEUE Chromium browsing soak plus a three-minute drain without a process restart or NFQUEUE queue/verdict errors. The observed memory footprint was bounded during that run; this is not a general leak-free or Score-optimality guarantee. IPv6 UDP/STUN remained unreachable through some tested proxy paths, including independent-client controls; this release does not claim to fix that path-specific limitation.
Full Changelog: v0.0.1.beta.80...v0.0.1.beta.81
v0.0.1.beta.80
Highlights
- Score now separates observed business reliability, configured-probe quality and fresh target performance, rather than favoring historical sample volume.
- Live response and transfer progress can inform Score before a connection ends; stale performance expires and validation stays bounded.
- A retryable Score-owned TCP setup may try one different permitted leaf within the original deadline, without replaying application payload.
- Clash-compatible APIs expose additive, read-only Score verification summaries and counters with explicit evidence scope and expiry.
What's Changed
New Features
- Add fresh-evidence ranking, bounded evidence-gap validation, live progress feedback and retractable Score verification by @Glassyiris in 060623f (#269).
Bug Fixes
- Exclude the failed primary before Score TCP recovery ranking, while preserving Selector boundaries, actual final-edge provenance, the shared deadline and dial admission by @Glassyiris in 060623f.
- Distinguish DNS-resolution timeouts before admission from genuine pending-admission capacity refusals, preserving eligible retry behavior by @Glassyiris in 060623f.
- Date recent business usability from positive response progress, not late terminal cleanup; preserve expiry and failure/reload fences by @Glassyiris in 060623f.
Documentation
- Update English/Chinese Score policy, control-plane and API documentation with evidence, recovery and verification boundaries by @Glassyiris in 060623f.
Upgrade Notes
No configuration schema migration is introduced relative to v0.0.1.beta.79.fix.fix. Score remains opt-in through policy: score; an omitted policy remains Selector. Existing Score groups can choose different winners because ranking and evidence aging changed. Performance expires after 120 seconds; business reliability retains its 30-minute half-life. Score state remains memory-only and resets on process restart.
Configured probes supply measurement quality, not business successes. Manual Clash delay results retain their API/Alive history but no longer feed the configured Score quality baseline or convert an arbitrary target failure into a real-dial failure strike. API consumers should tolerate the additive scoreVerification and verification-counter fields; these express bounded empirical observations, not guaranteed optimality or service-unlock capability. See the Score policy and API verification contract.
Full Changelog: v0.0.1.beta.79.fix.fix...v0.0.1.beta.80
v0.0.1.beta.79.fix.fix
Highlights
- Restore connectivity for REALITY peers that cannot use the hybrid ClientHello, without removing hybrid support required by current servers.
- Keep REALITY public-key/HMAC authentication mandatory: the compatibility connection is never ordinary-TLS success.
- Add dae parser conformance and fuzz-corpus replay coverage, and repair the documentation-example ranges used by that gate.
What's Changed
Bug Fixes
- Retry compatible REALITY peers once with fresh classical key shares, preserving same-peer bypass marking, one setup deadline, cold/pooled dial admission, and fail-closed authentication by @Glassyiris in 3e75feb (#258, fixes #257).
Testing and Documentation
- Compare dae-config parsing with dae, add authored dialect cases and replayable fuzz coverage by @Zakkaus in e6fe0d7 (#255).
- Correct shifted global-reference conformance ranges by @Zakkaus in 4dd51ad (#259).
Upgrade Notes
No new configuration fields or node migration are required from v0.0.1.beta.79.fix. The first REALITY attempt still offers hybrid shares. Only a completed TLS handshake presenting a non-Ed25519 leaf permits one fresh same-peer X25519-only connection; new random/key state and the same configured REALITY HMAC are required. Invalid Ed25519 HMAC, missing certificates, TLS/IO errors and HRR remain terminal. Both attempts share 3 × connect_timeout; an attacker or wrong credentials can induce the compatibility attempt, so this is not authenticated server-version detection.
Upgrading directly from v0.0.1.beta.79 also includes the previous DNS ownership and fragmentation changes: a local port-53 listener no longer preempts non-must LAN DNS; use explicit direct(must) for native resolver delivery or DNS policy/block(must) for rejection. Preserve persistent UDP/routing generation pins during ordinary restart.
Full Changelog: v0.0.1.beta.79.fix...v0.0.1.beta.79.fix.fix
v0.0.1.beta.79.fix
Highlights
- LAN TCP/UDP port 53 now follows honk traffic policy even when dnsmasq or another local DNS listener owns the destination. Explicit
direct(must),block(must)and raw-group decisions retain their meaning. - Fragmented LAN UDP DNS uses native kernel reassembly followed by NFQUEUE delivery, without a userspace reassembly cache or new configuration keys. Original packets are dropped exactly once before DNS/raw work is published.
- Harden queued DNS boundaries with UDP checksum validation, generation-safe reload/restart handling and fail-closed bridge escape protection.
What's Changed
Bug Fixes
- Route LAN DNS before local socket bypass and safely admit reassembled queries through NFQUEUE by @Glassyiris in 3b90784 (#256). This includes IPv4 fragment-field parsing, queue payload/checksum rejection, persistent routing-generation fencing and token-independent DNS admission.
Documentation
- Align English/Chinese routing, DNS, NFQUEUE and global configuration references; split real network regressions into isolated scenarios by @Glassyiris in 3b90784.
Verification
- Main CI passed for
3b90784, including real eBPF kernel lanes and cross-platform builds. - Deployed that main build to OpenWrt gateway
.118, with.117as the actual LAN client: 318 routing +161 DNS =479 effective rule assertions passed across the documented matcher/action families. - Detailed live rule matrix and explicit fixture/environment limits. Remote process-name positives were complemented by router-originated probes; the LAN rewrites DSCP to40, so exact DSCP values were verified before that rewrite and LAN rules against captured40.
- The canonical published aarch64-musl artifact was installed on
.118; version, SHA-256 and six additional real LAN UDP/TCP A/AAAA/HTTPS exchanges were verified. Original configuration and stopped service state were preserved. - A separate production-policy smoke completed 46/46 effective scenarios, including reordered IPv4/IPv6 DNS fragments and checksum rejection. External next hops were pinned and checked after DHCP changed the client default route. Initial fixture-only failures, startup reload timing and invalid route-precondition attempts are retained in the evidence; no source changes were made to force expectations.
- Original PR validation also included native IPv4/IPv6 fragment reassembly, invalid checksums, reload/restart generation fencing, and baseline/guard ablations.
Machine-readable LAN verification is attached as honk-v0.0.1.beta.79.fix-lan-verification.json. Implementation and the test harness were LLM-assisted under maintainer direction.
Breaking Behavior and Migration
Breaking change for deployments relying on local DNS socket precedence: a dnsmasq or dns.bind listener on gateway port 53 no longer exempts LAN queries from honk traffic policy. Valid intercepted TCP/UDP53 DNS with any non-must result (including ordinary direct, a proxy group, or block) enters honk's DNS controller. Controller ownership does not mean the upstream must use a proxy.
- Keep native DNS delivery: make the intended client/resolver rule select
direct(must)at the appropriate position in the ordered traffic policy. This bypasses transparent DNS processing and answer projection; a targeted local dnsmasq can receive the query normally. - Keep honk DNS policy and dnsmasq local/DHCP names: select dnsmasq's loopback listener as the DNS upstream for those names. Do not also configure that dnsmasq to forward the same queries back to honk. Honk's optional
dns.bindcan be disabled or use a free non-53 port; neither disables transparent interception. - Block DNS deliberately: use
dns.routing'srejectfor a DNS-policy rejection, or a traffic rule selectingblock(must)to drop traffic. Ordinary trafficblockdoes not remove valid DNS from controller ownership. To forward original DNS through a group without honk DNS policy/cache processing, select that configured group with(must). - Review broad native bypass rules: a private-network
direct(must)rule also bypasses private DNS. Add&& !dport(53)only if you intend that DNS to remain intercepted; honk does not rewrite your rule order or configuration.
See the routing ownership and migration reference and dnsmasq integration boundaries.
Upgrade Notes
- Fragmented LAN UDP/53 requiring controller or raw-group handling needs enabled, ready NFQUEUE. If unavailable, those fragments fail closed; native
direct(must)and unfragmented DNS are unchanged. This does not add TCP/IP-fragment support. - Preserve both
UDP_DECISION_SEQUENCEandROUTING_GENERATION_SEQUENCEpins during ordinary cleanup/restart. Do not delete routing-generation state while the host can retain old fragments. - Pure L2-transit DNS fragments are not assumed to reach inet NFQUEUE. Unsupported paths fail closed; honk does not automatically alter bridge firewall sysctls.
- An earlier ingress BPF program can still drop traffic before honk. The separate
.49experiment identified einat's out-of-order IPv4 fragment limitation; it was not changed by this release.
Full Changelog: v0.0.1.beta.79...v0.0.1.beta.79.fix
v0.0.1.beta.79
Highlights
- VLESS now separates UDP permission, packet encoding, and TCP/UDP multiplexing, with native UDP, XUDP, UoT v2, H2MUX, and independently controlled Xray pools.
- Breaking upgrade: migrate VLESS inputs before restarting. The removed
vless_modefield is rejected; default UDP behavior and all VLESS node identities change. - Gateway-address bypasses are now explicit user routing rules. Port-53 traffic respects terminal
mustownership, and unconfirmed LAN self-protection produces an advisory warning. - AnyTLS now bounds its writer queue in bytes; journald deployments can omit duplicate timestamps with
--disable-timestamp.
Breaking changes and migration
1. Migrate VLESS links and subscription content
Remove vless_mode completely, including vless_mode: null in structured input. It is not a deprecated alias. Static entries reject the candidate configuration; subscriptions discard invalid entries while retaining valid siblings. An entirely old-mode cached subscription cannot restore nodes offline, so obtain migrated provider content before upgrading offline.
Replace the old query parameter with the following canonical parameters; retain the existing endpoint, credentials, TLS/REALITY settings, and supported flow:
| beta.78 input | Replacement query |
|---|---|
vless_mode=legacy |
packetEncoding=auto&mux=off&udp=0 |
vless_mode=uot-v2 |
packetEncoding=uot-v2&mux=off&udp=1 |
vless_mode=h2mux |
packetEncoding=auto&mux=h2mux&padding=false&udp=1 |
vless_mode=h2mux-padded |
packetEncoding=auto&mux=h2mux&padding=true&udp=1 |
vless_mode=xudp |
packetEncoding=xudp&mux=off&udp=1 |
vless_mode=mux-cool |
packetEncoding=auto&mux=xray&concurrency=0&xudpConcurrency=0&xudpProxyUDP443=skip&udp=1 |
Previously omitted mode or packetEncoding=none was TCP-only in beta.78. Add udp=0 if you need to preserve that restriction. In this release, omitted UDP permission enables UDP, and packetEncoding=none selects native VLESS UDP rather than disabling it. Structured input uses network: "tcp" to disable UDP and the new packet_encoding / tagged multiplex fields; see the reference for exact shapes.
UDP/443 is now allowed by default, including base xtls-rprx-vision. To keep blocking QUIC, place this before broader matching rules:
l4proto(udp) && dport(443) -> blockFor Xray mux, xudpProxyUDP443=reject explicitly refuses UDP/443; skip selects the configured fallback packet encoding, not a deny. xtls-rprx-vision-udp443 normalizes to base Vision. Vision requires a direct TCP path; an Xray UDP-only pool is allowed, but TCP mux and H2MUX are not.
Every VLESS node gets a new derived identity on this upgrade. Runtime sessions and health must be rebuilt. Name-based saved Selector choices and qualifying TCP-v4 delays may restore; a restored delay does not restore liveness. Do not erase the cache just to migrate identities.
Use the canonical query spellings above. Ambiguous VLESS controls such as smux, multiplex, udp-over-tcp, packet-encoding, packet_encoding, packet-addr, xudp, and only-tcp are rejected rather than guessed. Repeated security/recognized tls claims must agree. REALITY intent requires a valid public key; remove contradictory inputs rather than relying on silent ordinary-TLS fallback.
2. Make gateway-management bypass rules explicit
Startup, reload, and network changes no longer insert interface-address direct(must) rules. Place explicit management-address rules before broader proxy/block rules, using your actual IPv4 and IPv6 addresses. For example:
routing {
dip(10.10.10.1, fd00:50::1) && !dport(53) -> direct(must)
# Existing user rules follow.
}Replace the example IPv6 address; do not copy it as your own. The !dport(53) clause leaves transparent DNS eligible for interception. A broad dip(geoip: private) -> direct(must) also bypasses private-address DNS unless you explicitly exclude port 53. The new self-protection warning is advisory, not a reachability guarantee.
Explicit local routing reference
3. Review terminal port-53 routing
For TCP and UDP port 53, direct(must) uses the native Linux path, block(must) drops, and group(must) relays the original traffic through that group without honk's DNS controller/cache/hosts/policy processing. Valid DNS queries with a non-must result—including ordinary block—remain owned by the DNS controller. Actual bound local sockets still take precedence. Use explicit terminal rules if the intent is to bypass or block DNS at the traffic-routing layer.
4. Restart with a matching userspace/eBPF generation
Upgrade honk-core, any separately built honk-tool, and any external --bpf-object together. Prefer the release binary's embedded eBPF object. Stop the old instance before starting the new one; do not run two real datapaths or mix an old handoff-map layout with new userspace. Retain the rollback binary/config and preserve UDP_DECISION_SEQUENCE; deleting it is not a normal upgrade or rollback step.
What's Changed
New Features
- Align VLESS UDP multiplexing and harden outbound boundaries by @Glassyiris in c38871c (#246).
- Warn when LAN self-protection is unconfirmed by @Glassyiris in c2cec87 (#250).
- Add
--disable-timestampfor journald-backed logs by @Zakkaus in 19d8125 (#252).
Bug Fixes
- Honor terminal traffic-rule ownership for DNS by @Glassyiris in cc68de0 (#247).
- Open a quote immediately after the configuration entry colon by @Zakkaus in 2a917dc (#248).
- Bound the AnyTLS writer queue in bytes by @Zakkaus in 8c2df0a (#254).
Documentation
Verification and benchmark limits
- Main CI passed on attempt 2. The initial Linux 6.12 VM job hit its 40-minute timeout; only that job was retried, with no test exclusions or source changes. The retry passed the complete floor-kernel gate; the original recent-kernel, x86_64/aarch64 workspace, lint, docs, feature, musl, and DNS smoke lanes passed. The intermittent floor-VM stall remains an observed CI limitation, not a diagnosed production fault.
- Tag workflow passed its workspace suite and all eight target/allocator builds. The canonical x86_64 musl mimalloc artifact was then deployed to VyOS; the same artifact separately passed the complete ten-row TCP / nine-row UDP datapath smoke on the dedicated lab host. Its binary SHA-256 is
5b3e465100f7b95d02f86d26ed37803ca0aca2eadc52493095533ea6353e712d. - Local outbound suite: 777 passed, 4 ignored; the two official Xray 26.9.9 / sing-box 1.13.19 VLESS tests were run separately and both passed. The first default-debug parallel build was killed; the successful rerun used one build job and disabled test debuginfo.
- Real eBPF/TPROXY lab smoke after benchmarking: TCP passed for SOCKS5, Shadowsocks AEAD, Shadowsocks 2022, Trojan, VMess-over-WebSocket, VLESS-over-TLS, AnyTLS, TUIC, Juicity, and Hysteria2. All nine UDP-capable rows passed ten exact 1200-byte echoes with source-address checks and per-group traffic-counter confirmation. VMess UDP is unsupported, not a passed test.
- Machine-readable benchmark and verification evidence includes the paired results, raw numeric UDP repeats, binary hashes, and verification scope. Fixture credentials and production configuration are not published.
- VyOS Linux 6.18.33 deployment: service active, configured proxy count preserved, NFQUEUE 320 active, TCP/UDP DNS queries passed, and live Hysteria2/SOCKS5/VLESS delay probes passed. The pre-tag candidate passed all 31 configured VLESS nodes in a live delay sweep. The final packaged-artifact sweep passed 30/31 initially; one node returned HTTP 503 and passed a separate immediate recheck (58 ms). That initial failure is retained in the verification evidence, not counted as a clean 31/31 first pass.
- The bench-branch harness compared the beta.78 musl mimalloc artifact with the pre-tag candidate, using 3 cold runs, 15 hot samples, three 8-second TCP throughput runs, and 200 loaded HTTP requests per route. Engine and targets shared a four-vCPU VM in separate network namespaces because the documented remote targets were unavailable. These are same-host synthetic results, not physical-router throughput or directly comparable historical lab numbers.
| TCP protocol | beta.78 Mbps | Candidate Mbps | Change | RSS MiB, before → after |
|---|---|---|---|---|
| Hysteria2 | 7857 | 7947 | +1.1% | 58 → 54 |
| TUIC | 7563 | 7990 | +5.6% | 59 → 59 |
| Shadowsocks 2022 | 27948 | 28073 | +0.4% | 62 → 48 |
| Trojan | 24361 | 24203 | −0.6% | 62 → 61 |
| AnyTLS | 11559 | 12152 | +5.1% | 55 → 49 |
Candidate loaded-latency failures were 0/1,200, versus 6/1,200 on the baseline (all six on TUIC). One paired sweep is not a statistical improvement claim. Saturated 10-Gbps UDP results were highly variable and lossy on both releases; three-repeat HY2/TUIC checks at 100 Mbps measured about 100.11 Mbps with about 0.39% iperf-reported loss on both. Do not interpret saturated UDP rows as a reliable line-rate or regression resu...
v0.0.1.beta.78
Highlights
- Source-backed DAE parsing and active-generation, redacted configuration diagnostics make configuration problems easier to locate without exposing credentials through the Clash API.
- Periodic HTTP health checks and URLTest now share request handling and warm-path timing, including configured paths/queries and a validated warm-sample fallback for HTTP/2 REFUSED_STREAM.
- SOCKS5 UDP setup has an overall deadline; selector changes during reload retain their persistence callbacks; subscription recovery checks store ownership and write permissions.
- Startup releases selected Geo source buffers once the traffic and DNS routers own their compiled state, instead of retaining those source files throughout the control-plane lifetime.
Upgrade Notes
- Review startup diagnostics and
GET /configs→honk-diagnosticsafter upgrading. The parser retains source attribution and explicit structural boundaries; malformed input should be corrected rather than relying on accidental parsing. - Subscription stores must be owned by the service UID and must not be group/other-writable. Symlink and non-regular cache entries are not trusted. Back up existing stores before correcting ownership or permissions.
- Health-check and URLTest results can change because the configured HTTP target is now measured consistently. No production RSS reduction or latency improvement is guaranteed for every configuration.
- Rust library full-reload callers now supply
DiagnosticBuckets; useDiagnosticBuckets::default()for programmatic inputs without diagnostics. - When upgrading from beta.76 or earlier, also review the beta.77 compatibility and certificate-verification notes.
What's Changed
New Features
- Rebuild the DAE parser around a shared lexer and bounded section readers, and expose active configuration diagnostics by @Zakkaus in 27a0561
Bug Fixes
- Share HTTP measurement across periodic health checks and URLTest while preserving configured request targets by @Glassyiris in ed67329
- Fall back to the validated warm sample on a remote HTTP/2 REFUSED_STREAM by @Zakkaus in 346e667
- Bound SOCKS5 UDP association, persist reload-window selector choices, validate subscription-store ownership, and retain the real UDP-to-TCP health mirror by @Zakkaus in f551e13
Performance
- Release startup Geo sources after router compilation by @Glassyiris in 780c3f1
Documentation
Build and CI
- Pin toolchains and inputs, bound and cache jobs, and run host suites under nextest by @Zakkaus in 92097cf
- Select affected lanes, add documentation links, label-triggered full coverage, weekly drift checks, packaged-artifact startup and CI reporting by @Zakkaus in f16215f
- Run the DNS process smoke against release honk-core in an independent lane by @Zakkaus in 5752c72
Dependencies
- Refresh the minor/patch dependency group by @dependabot[bot] in 109b7d9
- Upgrade Brotli to 9 by @dependabot[bot] in f2a8a75
Verification
- Main branch CI passed at
780c3f1, including workspace nextest, fmt/clippy, floor/recent real-kernel gates, native aarch64, independent feature checks, musl and release DNS smoke. - Release workflow passed the unfiltered workspace gate, all eight architecture/libc/allocator builds, packaged x86-64 startup checks and GitHub Release publication.
- The canonical x86-64 musl/mimalloc tarball was checked against GitHub's SHA-256 digest. Its static binary reports
honk-core v0.0.1.beta.78. - These checks are not an exhaustive physical-WAN or parameter matrix. Earlier-release compatibility and known-issue notes remain relevant when upgrading older deployments.
Full Changelog: v0.0.1.beta.77...v0.0.1.beta.78
v0.0.1.beta.77
Highlights
- Configuration readers now share option semantics, reject inconsistent node state, and return redacted, source-aware diagnostics. Review the compatibility notes before upgrading.
- Safer operational defaults and validation: the shipped example controller is loopback-only, DNS validation is stronger, and startup-captured health settings can no longer silently diverge on reload.
- Quieter per-connection logging, local-time timestamps with UTC offsets, corrected hexadecimal DSCP parsing, and reliable per-CPU map reads on sparse CPU topologies.
- Unchanged, already-admitted runtime configurations avoid redundant validation; subscription diagnostics are bounded without dropping later usable entries.
Potential Breaking Changes
This is an experimental beta. Configuration syntax has not been removed: DAE remains the primary format, and existing JSON/YAML/TOML readers and flat node keys remain available. The compatibility risks below come from changed interpretation, earlier validation, operational defaults, and Rust API contracts.
Security-sensitive certificate verification change
Review share links using allowInsecure, allow_insecure, or insecure before upgrading. These values now ignore surrounding whitespace; true, 1, yes, and on disable certificate verification. On ordinary links, yes/on previously left verification enabled, and whitespace-padded true/1 were not treated the same way. To keep verification enabled, remove conflicting aliases and use one explicit allowInsecure=false. Do not mechanically replace old yes/on with true. Native structured boolean fields remain typed; this is not a new string-to-boolean coercion rule for JSON/YAML/TOML.
Operator compatibility checklist
| Area | What may break or change | Upgrade action |
|---|---|---|
| Conflicting node options | Repeated or aliased credentials, TLS names/verification, stream transports, packet-network claims, VMess ciphers, and other consumed options can no longer silently overwrite conflicting values. Record tls-verification is checked as the inverse of an insecure flag. Explicit empty record credentials no longer silently fall back to positional credentials. |
Keep one authoritative spelling/value; preserve exact credential bytes. For example, tls-verification=true agrees with insecure=false, not insecure=true. |
| Node and collection admission | Invalid contained nodes are rejected earlier; runtime admission rejects stale/noncanonical or duplicate IDs and dangling group membership, nested groups, final targets, or DNS detours. Renaming a duplicate endpoint does not create a distinct identity. | Supply real UUIDs, explicit valid host/port fields and valid references. Inspect admitted node counts and diagnostics: recognized invalid DAE node lines and subscription entries can be skipped while valid siblings survive; invalid nodes in structured Config input can reject the load. |
| Protocol options and durations | TUIC share links reject non-native relay modes; HY2 share links reject unknown obfuscation names, and hopping sets reject repeated ports/overlapping ranges. Feed millisecond durations stored as seconds round up (500ms → 1s); negative/nonfinite/overflow values are no longer silently saturated. |
Match the actual server settings, use lowercase obfs=salamander with its correct password, remove duplicate hop ports, and use explicit integral seconds where exact timing matters. Do not remove an active security/transport requirement merely to make a node load. |
| Traffic rules and empty subgroups | Unsupported/malformed traffic predicates now fail configuration instead of disappearing and broadening a rule. Explicit filter: group() remains empty through serialization and refresh, rather than becoming an implicit all-node group. |
Correct the matcher and use explicit && conjunctions. To select all nodes, remove filter/nested-group constraints; to select a subgroup, name an existing one. |
| DNS policy | Undeclared upstream references are rejected during whole-config admission, including fallbacks even behind catch-all rules. Invalid/unsupported DAE DNS conditions omit the whole rule, with a diagnostic, instead of retaining a weakened rule. | Correct declarations and references, explicitly select a valid fallback when replacing the built-in upstream set, and review every DNS-rule warning. Merely parsing a Config fragment is not whole-config validation. |
| Subscription encoding and diagnostics | Fetched and Base64-decoded bodies must be valid UTF-8; invalid bytes are no longer lossily repaired. Current admission also applies when restoring older cached bodies. Diagnostics retain at most 128 nonterminal entries plus a truncation summary, not one retained warning per input line. | Export UTF-8, inspect skipped entries and truncation counts, and retain a working binary/config backup. Partial admission and first-usable duplicate selection already existed; this release does not introduce those policies. |
| Health settings on SIGHUP | A change to check_interval, the first HTTP-check URL, its active HTTP method, the selected UDP DNS target, or TLS↔uTLS mode now rejects the reload and keeps the active configuration/probes. Invalid udp_check_dns entries can reject admission even when unselected. |
Restart the process for genuine startup-owned setting changes. Later HTTP URLs and unselected valid UDP targets are not equivalent to changing the active target; see the exact comparison rules in the global reference. |
| HTTP health targets | Authority parsing now handles userinfo, bracketed IPv6 and non-default ports correctly, and rejects surplus authority slashes, backslashes, ASCII whitespace/control characters, and other malformed inputs. Invalid global HTTP-check URLs may fall back to raw TCP probing with a warning. | Use valid explicit HTTP/HTTPS URLs and verify that an HTTP check, not a TCP fallback, is running. URL userinfo does not add Basic authentication; URLTest still measures HEAD /, not the supplied path. |
| Shipped controller bind | The root config.dae example changes from 0.0.0.0:9090 to 127.0.0.1:9090. Adopting that example removes direct remote dashboard/API access. Existing custom configurations are not rewritten; a non-loopback bind without a secret warns but is still permitted. |
Prefer SSH forwarding or an authenticated TLS reverse proxy. If LAN access is intentional, configure the address, a strong secret, and firewall restrictions explicitly. |
| Log/diagnostic consumers | Per-connection routing lines move from INFO to DEBUG. Console/file timestamps use local time with a numeric UTC offset. Diagnostic prose/values are now redacted and located differently; compatibility diagnostic vectors may contain failure causes, not just warnings. | Update parsers/alerts, enable an appropriate debug filter when auditing connections, and consume detailed diagnostic code/path/severity/terminal fields instead of old prose or raw scalar values. |
honk-tool UDP DNS hostname probes |
A failed/empty configured-DNS result no longer escapes to the bootstrap/system resolver. Names that only worked through that fallback may now report a DNS-column failure. The asynchronous first-nameserver/hosts setup was already present in beta.76. | Use a literal --udp-check target or a name resolvable through the first numeric /etc/resolv.conf nameserver and hosts snapshot. Do not depend on NSS/search-suffix fallback for this path. |
Rust library consumers
RuntimeRegistryErroris source-incompatible: the oldNilId,DuplicateId, and struct-styleTls { node, source }variants are replaced byAdmission(DetailedConfigError)and tupleTls(Option<std::io::Error>). Update downstream matches/construction.NodeRuntime::ephemeral,ephemeral_guarded, andurltest::probe_runtimekeep their signatures but can now panic on invalid or stale-ID input. Prefertry_ephemeral,try_ephemeral_guarded, andtry_probe_runtimeand handle theirResult.- Validate programmatically constructed nodes, set
node.id = node.derive_id()after identity-affecting edits, and supply fully assembled valid references to runtime/control-plane construction. This is stronger enforcement, not another change to the node-ID hash algorithm. - Legacy error projections retain categories but do not promise the original raw value, OS errno, or source-error chain. Migrate consumers that relied on those details to the detailed error/diagnostic APIs rather than matching old error prose.
Back up the active binary and configuration, exercise a copy in an isolated environment, review startup/reload diagnostics and effective membership, then restart the single real datapath instance when required. Detailed references: nodes, global/reload, DNS, subscriptions, and 中文文档.
What's Changed
New Features
- Unify configuration option semantics, admission, and redacted diagnostic APIs by @Zakkaus in 27426b2
Bug Fixes
- Prevent honk-tool UDP DNS resolution from falling through to the system/bootstrap resolver by @Glassyiris in f7e5a8e
- Parse hexadecimal DSCP and omit unreachable routing code by @Glassyiris in 46cfac1
- Si...