Skip to content

Subscription reference ​

subscription {} configures remote node lists.

subscription {} syntax ​

Declare the URL on the entry line. To override download settings for one subscription, add a block after the quoted URL, one key per line:

dae
subscription {
    primary: 'https://example.com/sub'
    'https://example.com/no_tag_link'
    detailed: 'https://example.org/sub' {
        ua: 'honk/1.0'
        interval: 10000s
        cache: false
        route: direct
    }
}

The entry block accepts four options:

OptionEffectBuilt-in default
uaOverride the request User-Agent.honk/<version>
intervalSet the refresh duration; 0 disables periodic refresh.86400s
cachefalse keeps this subscription's body out of the subscription store.On
routeSelect the fetch route (see below).routing

A missing option uses the assets.subscription default, or assets.route for the route, before the built-in default. See the assets reference.

Two earlier forms still parse without a warning:

FormExample
(UA) suffix after a quoted URLcompatible: 'https://example.net/sub'(honk/1.0 like)
URL inside a blockdetailed: { url: '…' ua: '…' }

The block also accepts download_detour as an alias for route. Setting both fails with conflicting-subscription-route. The suffix and joined-comment rules below apply to the (UA) form.

Tags are optional. Tags and URLs may use matching single or double quotes.

Entry formTag parsing
Bare entryText before the first : is the tag, unless that colon begins ://. Later URL colons do not split a tag.
Quoted tag followed by :Explicit tag.
Quoted URL without an explicit tagRemove the enclosing quotes, then apply the first-colon rule. 'paid:https://example.com/sub' has tag paid; 'https://example.com/sub' is tagless.

The (UA) suffix requires a quoted URL, so parentheses in bare URLs remain unambiguous. Both forms retain sub_type: simple, which automatically detects the supported body formats below.

The link suffix is parsed before embedded-tag normalization: 'paid:https://example.com/sub'(agent) and 'paid:https://example.com/sub'#note retain tag paid. Diagnostic entry ordinals count both block and inline declarations in source order, including skipped entries. For a quoted head, only a colon immediately after the closing quote (allowing whitespace) declares an explicit tag. Colons in an unquoted User-Agent or glued comment suffix remain suffix data: 'https://example.com/sub'(agent:1) is still tagless.

A tagless entry uses its URL host as its name: 'https://example.com/sub' becomes example.com. A URL without a parseable host leaves the name empty and fails validation. Name uniqueness is not enforced: an explicit example.com tag and a tagless URL on that host share the same subtag(example.com) filter, which selects nodes from both subscriptions. Host-derived naming applies only to dae; JSON, YAML, and TOML still require name.

Tagless text without :// is ignored. Explicitly tagged entries still reach validation, which requires a non-empty name and an HTTP(S) URL. file://, http-file://, and https-file:// remain unsupported.

On entry lines, # starts a comment outside matching quotes at the start of the statement or immediately after an ASCII space or tab. In a bare URL, a glued # remains data, including after parentheses: https://example.com/sub?filter=(hk)#token. Quote a User-Agent containing a spaced #: 'https://example.com/sub'('agent # build'); without UA quotes, the comment cuts off the suffix.

After a quoted URL, a glued # immediately after the closing quote or one balanced (UA) suffix is accepted as a comment, with legacy-glued-hash at that byte and the guidance “put whitespace before a comment”. 'http://q'#c and 'http://q'(ua)#c keep URL http://q, with UA ua only in the second form. Parentheses inside matching quotes do not affect nesting. A glued # inside the suffix remains UA data, including in (Mozilla/5.0 (X11; (Linux)#build)). Other trailing text, including (ua)(x) or (agent) junk, skips the entry with trailing-entry-text or legacy-ua-boundary.

This glued-tail compatibility runs after block recognition; it is not a lexical comment. In sub: 'http://q'(ua)# }, the separated } still closes the subscription block, so subsequent entries can fall outside it. Write (ua) # } to make the brace comment data instead.

Quote-error and block rules are listed in the dialect reference. An empty route follows routing, in every build.

Internal model ​

FieldTypeDefaultSettable in daeMeaning
idUUIDrandom UUIDNoRuntime subscription identity; SIGHUP preserves it when the fetch identity (URL + configured ua + headers) matches an existing subscription.
namestring""Yes, as the tag; otherwise the URL hostDisplay tag and the value used by group subtag(...) filters.
urlstring""YesHTTP(S) fetch URL.
sub_typeenumsimpleNoBody parser: simple, clash, sip008, or custom.
update_intervalu6486400Yes, as intervalPeriodic refresh interval in seconds; 0 disables periodic refresh.
user_agentstring or nullhonk/<version>Yes, as uaOptional User-Agent override; otherwise requests identify as honk/<version>.
headers{key,value}[][]NoOrdered extra request headers.
download_detourstring""Yes, as routeFetch route: empty or routing follows the routing rules, direct connects directly to the host, and a group name forces that group. An unknown group is rejected during validation.
enabledbooltrueNoDisabled subscriptions are not restored, fetched, or refreshed.
cachebooltrueYes, as cacheWith global.store_subscribe, keep the fetched body for offline startup. false neither stores nor restores it, and maintenance deletes a body kept earlier.
last_updateddatetime or nullnullNoModel metadata; the current core runtime does not update it.
node_countu320NoModel metadata; the current core runtime does not update it.
created_atdatetimeconstruction timeNoModel construction time.

Body parser selection:

sub_typeParser behavior
simpleDetects share-link lists, Clash YAML/JSON, SIP008, sing-box JSON, and supported client records.
clashYAML or JSON with a top-level proxies sequence.
sip008SIP008 servers objects or a bare server array; not a share-link list.
customThe same format detection as simple.

honk-tool sub uses the same parser for downloaded bodies and local files.

Fetch, persistence, and recovery ​

global.store_subscribe defaults to true. When enabled, the runtime opens a private subscription store and fetches every enabled subscription immediately. Requests identify as honk/<version> unless user_agent supplies an override. A non-zero update_interval schedules later refreshes.

PropertyCurrent behavior
LocationThe subscription_body table of the state db, <data_dir>/state/honk.db (0600 file in a 0700 directory; see the API reference); the default data_dir is /var/lib/honk.
KeyURL-safe Base64 of a SHA-256 hash over the length-delimited URL, configured user-agent override (empty when unset or empty), and ordered header key/value pairs, plus .sub. The versioned default request UA is intentionally not part of the key, so default subscriptions retain their body across upgrades. The request identity is not exposed in plaintext.
Write boundaryAfter HTTP success and body acceptance, the complete raw response, including rejected entries, is written in one transaction.
LimitsEach body is at most 8 MiB, and all bodies together at most 32 MiB. A write that would pass 32 MiB first deletes bodies of subscriptions that are no longer enabled; if that is not enough it is refused, the previous body stays, and the fetch reports subscription-store-write-failed. A body whose subscription is not enabled at two consecutive maintenance ticks (every 60 seconds) is deleted, and a start with store_subscribe: false that still opens the state db, for cache_file, password_auth or --store db, empties the table.
Upgrade from .subImport eligible legacy bodies after acquiring the instance lock, as described below.
RedirectsAt most 5 hops. A redirect from https to another scheme fails the fetch, as does one to a loopback, private, link-local, or unspecified literal address that the configured URL did not itself use. A hostname resolving to such an address is not detected.
Body sizeAt most 8 MiB, enforced while reading rather than after the body is buffered.

Legacy .sub import runs at startup after the instance lock is held:

  1. Select the first private legacy store in <data_dir>/.sub, /var/share/honk/.sub, then ./.sub.
  2. Copy bodies for enabled subscriptions, retaining bodies already in the state db.
  3. Unlink copied *.sub files and every .*.tmp file in that directory. Remove the directory if empty. Leave other legacy locations unchanged.

Unreadable bodies, bodies over 8 MiB, and bodies that would exceed the 32 MiB total remain in place with a warning. Disabled subscriptions' bodies also remain. A later startup that enables such a subscription can import its body without fetching it.

The previous process has exited before import, so its final body writes are copied like the others. An older binary started afterward finds only the bodies left behind and fetches the others again.

Subscription bodies and their nodes remain runtime state. Neither is written back into the dae configuration.

Subscription fetches follow routing unless the entry's route or assets.route overrides it, as with other downloads initiated by honk.

RouteBehavior
Empty or routingApply traffic-routing rules to each URL, including redirects. Rules may select a node, group, direct, or block.
Group nameForce that group.
directUse the bootstrap resolver and bypass mark, without routing. This retains the previous direct transport.

Routed requests share route decisions and tunnels with geodata and external UI downloads. They retain the same User-Agent, headers and download constraints:

ConstraintBehavior
Timeout and body size30 seconds; at most 8 MiB.
Redirect statusOnly 301, 302, 303, 307 and 308; at most 5 hops.
Redirect destinationNever HTTPS to HTTP, or a public address to a private literal address. The redirect restrictions in the preceding table also apply.
URL userinfoSent as basic authentication.
Change of scheme, host or portDrop Authorization, Cookie and Proxy-Authorization, as the direct client does.

Builds without native-api route subscription fetches identically.

A subscription can route through nodes it supplies itself, for example when the rules send its URL to a group made only of its own nodes. On a fresh install those nodes do not exist yet. honk never falls back to direct: when the chosen route has no usable node, the fetch fails with an error that names the subscription and the outbound, says the route cannot carry the download yet, and suggests route: direct for that subscription. The provider status in the native API reports last_error.code route_unavailable. Nodes restored from the stored body stay in service throughout.

Startup restores nodes before refreshing subscriptions:

  1. Parse stored bodies before starting network refreshes. A valid restored body supplies active nodes immediately. Missing, invalid or empty stored bodies are ignored.
  2. Wait for direct subscriptions without a valid restore to finish fetching, within one shared five-second deadline. A valid restore excludes that subscription from the wait; its refresh still runs in the background.
  3. Make routing ready after the startup subscription pass, then fetch routed subscriptions.

A later accepted refresh replaces the SQLite body row, not a legacy file.

If a restored or first-fetched body contains a node ID already used by an inline node or another subscription, startup excludes that subscription instead of failing. Runtime publication rejects the same collision. A body may still replace its own subscription's nodes.

The native API provider reports last_error.code as publication_rejected and last_error.details.diagnostic_code as duplicate-node-id. A restored body rejected this way still counts as restored: it skips the first-fetch wait. Background refresh still runs and repeats the check.

On SIGHUP, subscriptions with the same fetch identity (URL + configured ua + headers) retain their runtime ID. The reload carries active nodes belonging to still-enabled subscriptions, commits the rebuilt configuration, and then starts an immediate background refresh. It does not read stored bodies, even when no nodes survive for a subscription; stored-body recovery runs only at startup.

Failure handling preserves a usable runtime rather than clearing it:

  • HTTP, invalid UTF-8 encoding, parse, or no-usable-node failure publishes no replacement nodes and performs no write, so the active nodes and last valid stored body remain. Accepted bodies are persisted byte-for-byte, without repairing encoding.
  • A persistence-write failure is non-fatal after parsing: the newly parsed nodes are still returned for publication, while the atomic path never installs a partially written body. The next restart can therefore restore whichever complete valid body remains on disk.
  • An unsupported or malformed node is skipped individually. The shared node builder's warning includes a one-based proxy index and a static rejection reason, never the raw record or its credentials. The whole body fails only when no usable nodes remain; an empty result never clears the previous generation.

The last valid body is the last body accepted by the current import policy, not the last successfully published runtime generation. A partial body with one distinct usable node may replace the stored body; an all-invalid body cannot. Restore reparses with the current policy and may reject a body saved by an older version. Runtime collection or publication failure after a successful write retains the active generation but can leave the new body on disk. Disk and active state are not one transaction.

Changing global.store_subscribe through SIGHUP is rejected as restart-required.

First start when the subscription URL is blocked ​

If the subscription URL is blocked or its domain is poisoned, add a reachable static proxy node and set the subscription's route to a group containing only that node. This group is available on first start without nodes from any subscription. Replace the SOCKS5 address below with a proxy that can reach the provider, and replace the subscription URL with your own.

dae
node {
    bootstrap: 'socks5://192.168.1.2:1080'
}

group {
    Bootstrap {
        filter: name(bootstrap)
    }
}

subscription {
    provider: 'https://example.com/sub' {
        route: Bootstrap
    }
}

Subscription body formats ​

All accepted nodes receive the subscription ID. Duplicate derived node IDs retain the first usable occurrence; rejected entries do not reserve an identity. Later duplicates report both original indices. Array indices are one-based before normalization; URI and record indices are physical lines, including blanks and comments. Decoded Base64 has a child source referring to its original body, not fabricated encoded-byte offsets. Importing a full client profile extracts its nodes, not its DNS, routing, groups, or remote-provider configuration.

Normalized entries are admitted incrementally, without retaining a body-sized list of outcomes. Each body retains at most 128 nonterminal diagnostics, followed by one subscription-diagnostics-truncated summary with the omitted count; a terminal body failure is retained separately. The caller's existing diagnostic prefix is untouched and does not consume this budget. The limit applies to structured, URI, and record imports, including decoded Base64 and restored bodies, and never stops valid-node admission or changes first-usable duplicate selection.

Clash, SIP008, and sing-box credential scalars preserve string bytes and convert native finite numbers to the source format's canonical decimal text. Missing or null values are absent; booleans, lists, maps, and nonfinite numbers reject the entry. A valid alias cannot hide an invalid supplied credential. Numeric UUIDs still fail UUID validation; empty passwords remain subject to each protocol's requirements.

A list can be plain text or standard/URL-safe Base64, with optional padding and ASCII whitespace between encoded chunks. A leading UTF-8 BOM is accepted in raw and decoded bodies. Each line contains one share link:

text
# blank lines and comments are ignored
socks5://user:[email protected]:1080#local
vless://[email protected]:443?security=tls#edge

Blank lines, comments, and Shadowrocket REMARKS= / STATUS= metadata lines are ignored without node warnings. Every remaining line is parsed by Node::from_share_link; unsupported or malformed lines are skipped with a warning. Share links with a non-empty proxy plugin are also skipped because honk does not execute plugins. The body is rejected when it contains no supported node URI, including metadata-only bodies. See the node reference for canonical share-link fields and protocols.

Clash YAML and JSON ​

A full profile or provider payload must contain a top-level proxies sequence. Entries without a supported type, server address, valid nonzero port, or required credentials are skipped. Ports may be integers or numeric strings. JSON is decoded natively, including UTF-16 surrogate-pair escapes in display names.

Accepted type values are socks5, ss/shadowsocks, trojan, vmess, vless, hysteria2/hysteria, tuic, juicity, and anytls. Unrelated client metadata is ignored; unsupported wire transports, proxy plugins, and contradictory security settings are not silently reinterpreted.

Common proxy fields ​

Clash fieldInternal fieldRule
namenameDefaults to <type>-<server>:<port>.
server, porthost, port, addressRequired address and nonzero u16 port; numeric port strings are accepted.
usernameusernameOptional string.
passwordpasswordOptional string; VLESS applies the precedence below.
cipherencryptionOptional string; VLESS applies the precedence below.
plugin, plugin-opts—Unsupported. An entry with either non-empty value is skipped before node publication; mapping-valued options are rejected too.
networktransport or packet capabilityTrojan/VMess/VLESS use a stream transport (tcp, ws, grpc, or xhttp); AnyTLS uses packet capability.
tlstlsOptional boolean. Trojan, AnyTLS, Hysteria2, TUIC, and Juicity default to TLS and reject explicit disabling.
servername, server-name, snisniEmpty or whitespace-only names are absent; nonempty aliases must agree byte for byte.
skip-cert-verify, skip_cert_verify, insecureskip_cert_verifyNative booleans; supplied aliases must agree.
alpntls_alpnOrdered string list (or comma-separated string) for raw-TCP TLS; XHTTP accepts only H2 and normalizes omission to ["h2"]. QUIC retains the protocol-specific rules below.

Protocol-specific options ​

Supported spelling aliases normalize before node identity is derived.

ProtocolImported options
Hysteria2password/auth; obfs: salamander with obfs-password; upload/download bandwidth; ports/mport hopping ranges; hop-interval/mhop; receive windows; MTU and MTU-discovery settings.
TUICUUID/password, congestion control, ALPN, receive windows, MTU.
AnyTLSidle-session-check-interval, idle-session-timeout, min-idle-session.

Explicit disabled feature blocks are treated as disabled, not as unsupported active features. udp: true is accepted for intrinsically UDP-capable protocols. An explicit UDP restriction is rejected where the node model cannot preserve it.

TUIC permits an absent or empty password. Hysteria2 and Juicity accept an explicit h3 ALPN matching their fixed runtime selection.

Juicity starts with 8 MiB stream and connection receive windows; adaptive flow control can raise each of them up to 32 MiB. Imported initial-window fields accept absent values, 0, or 8388608; other overrides are rejected.

AnyTLS packet claims are resolved once in this order: anytls-network, applicable network, then udp. Empty or null network text supplies no claim. Network strings use comma-separated tcp/udp tokens; aliases are compared by UDP allowance, so udp and tcp,udp agree, while tcp with udp: true conflicts. Unknown tokens such as quic reject the entry even beside a valid alias. Equivalent claims retain the first explicit network spelling; only a boolean-only input synthesizes tcp or tcp,udp.

TCP TLS ALPN rules apply to structured subscription imports and the flat tls_alpn field. Share-link ALPN compatibility is unchanged.

Input or constraintBehavior
List membersPreserve bytes and order. Each name occupies 1–255 UTF-8 bytes.
Length-prefixed listAt most 65,533 bytes. The complete ClientHello also has TLS-library size limits.
Imported alpn absent, null, or emptyPreserve existing TLS-profile defaults and node IDs.
Flat tls_alpnAccept omission or a string array, not null.
Nonempty overrideParticipate in node identity. Reject with disabled TLS, REALITY, WebSocket, or gRPC instead of discarding it.
Chrome ALPSOffer only when the actual ALPN list contains h2.

XHTTP is the exception to the raw-TCP ALPN rule: it always uses H2, including REALITY and explicit cleartext H2. Mihomo accepts network: xhttp or the input alias splithttp.

xhttp-opts fieldsImport behavior
path, host, mode, headers, x-padding-bytes, no-grpc-header, sc-max-each-post-bytes, sc-min-posts-interval-msAccepted.
no-sse-header, sc-max-buffered-posts, sc-stream-up-server-secsServer-only; type-checked and dropped.
reuse-settings, xmuxFollow the URI xmux rule with Xray's meaning. Mihomo instead enables its own reuse manager whenever reuse-settings is present.
download-settingsMust be an Xray StreamConfig mapping, as described below.

download-settings accepts the panel-exported Xray shape: address, port, network, security, alpn, tlsSettings, and xhttpSettings. It may also contain flat x-padding-bytes, upload-only no-grpc-header/sc-* keys, and default reuse-settings/xmux. It has the same meaning as URI downloadSettings, so the two exports of one subscription produce the same node IDs.

Mihomo's inheriting keys, such as server, servername, tls, and skip-cert-verify, reject. This intentionally differs from mihomo, which ignores Xray-shaped keys and sends GETs to the upload server. See the XHTTP node reference for canonical defaults, bounds and header restrictions.

Other unsupported options, including alternate placements and padding-obfuscation settings, reject the entry by raw presence, even when null, empty or false. Valid sibling nodes survive. URI/VMess extra JSON rejects duplicate members before map construction.

VLESS transport and REALITY ​

VLESS fields are applied before node identity is derived:

Clash inputMapping
uuid, then passwordCredential; uuid wins and password is the fallback.
encryption, then cipherVLESS Encryption; encryption wins.
flowEmpty or whitespace-only is absent; otherwise exactly xtls-rprx-vision or xtls-rprx-vision-udp443.
networkStream transport. Packet permission remains the separate udp claim.
reality-opts.public-keyEnables the REALITY TLS carrier. It must be a non-empty string.
reality-opts.short-idOptional REALITY short ID.
reality-opts.spider-xREALITY spider path; missing or empty becomes /.
ws-opts.pathWebSocket path; falls back to flat ws-path.
ws-opts.headers.HostWebSocket Host header, with case-insensitive key matching; falls back to scalar ws-headers, then ws-host.
grpc-opts.grpc-service-namegRPC service name; falls back to grpc-service.
client-fingerprintIntentionally not imported. TLS fingerprint selection is process-wide through global.tls_implementation and global.utls_imitate.

Nested WS/gRPC values take precedence over their flat aliases. Active reality-opts must be a mapping with a non-empty public-key; an invalid active declaration is never downgraded to ordinary TLS. Empty or explicitly disabled feature blocks are ignored. Imported REALITY uses the same TLS-1.3-only, hybrid-then-classic key-share and fail-closed one-ClientHello behavior as a canonical share link; see the node reference.

VLESS UDP and multiplexing ​

Import defaults are source-specific. They do not all mean honk's canonical packetEncoding=auto default.

Clash representationPacket fallbackMultiplexUDP permission
No packet declaration and no enabled wrapperautooffdisabled
Only an empty/null packet encodingautooffdisabled
packet-encoding: none/legacy, or xudp: falsenativeoffenabled unless udp: false
packet-encoding: xudp, or xudp: trueSingle XUDPoffenabled unless udp: false
udp: true without another packet declarationSingle XUDPoffenabled
udp-over-tcp: true or enabled object with version 0/2UoT v2offenabled unless udp: false
enabled smux/multiplex with protocol: h2mux or explicit paddingauto (inactive while wrapped)H2MUXenabled unless udp: false
mux: { enabled: true, ... }auto, unless an explicit packet encoding remains reachable through protocol fallback or skipXray Mux.Coolenabled unless udp: false

For H2MUX, padding: true selects the existing padded wire format; false selects unpadded H2MUX. Existing only-tcp, Brutal, and nonzero max-connections/min-streams/max-streams restrictions remain. Enabled H2MUX conflicts with Xray mux and UoT; enabled UoT also conflicts with Xray mux. Explicit XUDP conflicts with H2MUX or UoT, while their native/auto declarations are inactive behind the selected wrapper.

An enabled Clash mux block accepts active settings only for enabled, signed-i16 concurrency, signed-i16 xudpConcurrency, and xudpProxyUDP443. Inactive extras are ignored.

Concurrency valueTCP concurrencyxudpConcurrency
NegativeDisable TCP mux.Use the packet fallback.
ZeroAllow 8 concurrent logical children per TCP carrier.Share the enabled TCP pool and its per-carrier concurrency. If TCP mux is disabled, use the packet fallback.
PositiveSet per-carrier logical-child concurrency, capped at 128.Create a separate UDP pool with this per-carrier logical-child concurrency, capped at 128.

Concurrency never specifies a physical-carrier count. UDP/443 policy defaults to allow and also accepts skip or reject. See the node reference for precedence.

udp: false disables packet dialing independently; it does not disable an eligible bare TCP path. A VLESS Clash entry is also rejected for conflicting aliases, duplicate XUDP representations, enabled packet-address mode, unsupported packet encoding, unsupported UoT version, or an invalid/unsupported active Xray mux setting.

Canonical VLESS share links use exact packetEncoding=auto|none|xudp|uot-v2, mux=off|h2mux|xray, and udp=0|1 queries. The removed vless_mode query and ambiguous third-party share-link spellings such as smux, udp-over-tcp, and packet-encoding are rejected rather than guessed.

SIP008 and sing-box JSON ​

SIP008 version 1/2 wrappers ({"servers":[...]}) and bare server arrays import Shadowsocks server, server_port, method, password, and remarks. Empty plugin fields are harmless; active plugins remain unsupported.

sing-box profiles import supported entries from outbounds:

  • Proxy nodes: Shadowsocks, SOCKS5, VMess, VLESS, Trojan, Hysteria2, TUIC, Juicity, and AnyTLS.
  • Structural entries, not proxy nodes: selector, urltest, direct, block, and dns.

TLS/SNI, REALITY, WebSocket/gRPC, VLESS packet choices, and supported protocol tuning normalize through the common node builder.

Source-specific caseImport behavior
VLESS without an enabled H2MUX/UoT wrapper or explicit packet_encoding selecting another pathSingle XUDP with UDP permitted. This is a sing-box import default, not the global meaning of Auto.
Empty or omitted gRPC service nameRetain sing-box's empty service, not honk's GunService default.
Hysteria2 server_ports without server_portUse the first hopping port as the nominal endpoint.
Per-node uTLS fingerprint hintDoes not override honk's process-wide TLS selection.

Unsupported chaining, wire features, and authentication requirements are not silently discarded.

In sing-box input, network is packet capability, not a stream type; transport.type selects the stream. Unsupported stream names such as h2 reject the entry rather than becoming raw TCP.

Official sing-box has no native XHTTP transport. This importer explicitly rejects transport.type: xhttp and splithttp; it does not reinterpret old http, H2, QUIC or a third-party fork as XHTTP. Client-record XHTTP transports are likewise unsupported.

Where the sing-box mapping supports packet restrictions, network: udp and network: tcp,udp permit UDP, while network: tcp disables it. These values do not introduce UDP-only TCP rejection. network remains independent of VLESS fallback encoding and H2MUX/UoT carrier selection.

Explicit sing-box native VLESS UDP (packet_encoding: "") maps to native. Enabled H2MUX or UoT v2 owns the packet path even when the source also specifies packet_encoding, including "xudp"; disabling a wrapper does not suppress the sing-box Single-XUDP default. Sing-box multiplex import remains H2MUX-only; it does not synthesize Xray Mux.Cool controls.

Surge, Surfboard, Loon, and Quantumult X ​

The importer accepts named comma-separated records from Surge/Surfboard/Loon and protocol=endpoint,...,tag=name records from Quantumult X. Full profiles use [Proxy] or [server_local]; other sections are ignored. Quoted names/passwords may contain commas, equals signs, escaped quotes, and intentional edge spaces.

Explicit credential and cipher aliases must agree before conversion. In named records, positional credentials are used only when the corresponding named claim is absent; a different positional value does not conflict with a valid explicit claim. Quantumult X has no positional fallback. Empty explicit credentials participate in alias comparison rather than selecting a fallback.

Permitted optional record credentials retain empty strings and quoted whitespace byte for byte: SOCKS username/password, Hysteria2 authentication, and TUIC password. An explicit empty value still overrides a positional fallback; protocols requiring nonempty credentials continue to reject it.

Record transport/network stream claims are compared before assignment, including repeated keys. Empty text and tcp have the same raw-TCP meaning; conflicting or unsupported stream claims reject the entry.

AnyTLS record network occurrences are packet claims, not stream claims. Every repeated network, udp, and udp-relay value is validated before selection. Equivalent packet claims retain the first explicit network spelling; an invalid or conflicting occurrence rejects the record.

VLESS record packet-encoding/packet_encoding/packetencoding values none or empty select native UDP; xudp selects Single XUDP. Repeated aliases must agree, including explicit empty assignments. udp/udp-relay controls packet permission separately. With neither a packet encoding nor udp: true, a record remains UDP-disabled; udp: true without an encoding selects Single XUDP. Active record mux and UoT declarations remain unsupported rather than being reinterpreted.

Supported records map credentials, TLS/SNI, WebSocket/gRPC, REALITY, and implemented protocol options to the same node model. Quantumult X obfs=wss uses obfs-host for both WebSocket Host and the default TLS SNI; an explicit TLS hostname wins. SSR, unsupported plugins/obfuscation, and unsupported transports are skipped rather than imported as another protocol.

Surge server-cert-fingerprint-sha256 maps to honk's leaf-certificate pin: both replace standard X.509 verification. Independent server-cert-verify-name, client certificates, sni=off, and Shadow TLS are not representable and are rejected rather than discarded (Surge TLS reference).

Record aliases skip-cert-verify, allow-insecure, and insecure are compared with the inverse of tls-verification before assignment. tls-verification=false,insecure=true agrees; tls-verification=true,insecure=true rejects the entry. Record text accepts true/yes/1/on and false/no/0/off, case-insensitively; empty text and t/y/f/n remain invalid. Certificate-pin restrictions still apply.

Effective Quantumult X tls-cert-sha256 and tls-pubkey-sha256 pins are rejected: honk's leaf-certificate pin replaces PKI, and it is not substituted for an unverified foreign verification contract. With explicit tls-verification=false, QX ignores both pins and the import preserves that disabled verification. Effective QX REALITY ignores customized tls-alpn and session-ticket settings, as its official configuration specifies; ordinary TLS does not. Legacy VMess aead=false, active Shadowsocks UoT/SSR, unsupported TLS ALPN, and disabled TLS-session reuse are rejected rather than discarded.

Offline parsing and probes ​

honk-tool sub accepts a fetched subscription URL or a local file in any supported body format. A local file avoids the subscription download and is useful for offline parsing, but the command then performs its configured connectivity and latency probes:

console
honk-tool sub ./share-links.txt --limit 10
honk-tool sub https://example.com/sub --ua honk-tool

Downloaded bodies and local files use the same automatic detection. Passing - reads one HTTP(S) subscription URL from standard input; it does not read a subscription body from standard input. See the CLI reference for probe flags and output.


Source: honk upstream, licensed under GPL-3.0-only.