Remote rule sources
Remote sources are optional. Local sources continue to work without network access. The example below uses placeholder URLs and is illustrative, not a ready-to-run configuration.
Use url to load a remote source. Persistent caching is optional. The same
source object works in rule_source_group.rules, under masks (including
all/any/not), or flat alongside action in a policy group’s rules, a server’s
rules, or supported override_rules. A flat source cannot also specify masks.
- global_settings: rule_cache_dir: ./cache
- rule_source_group: regional rules: - url: https://example.net/geoip-cn.srs update_interval: 7d download: client_chain: direct bootstrap_url: system
- address: "0.0.0.0:1080" protocol: {type: socks} rules: - masks: {rule_source_group: regional} action: block - url: https://example.net/blocked.json download: client_chain: address: "proxy.example.net:1080" protocol: {type: socks} bootstrap_url: system action: block - masks: {any_destination: true} action: allowReplace proxy.example.net:1080 with your proxy address. download.client_chain accepts
the existing chain, pool, inline-client, and group syntax. It is independent of
the outer policy’s client_chain, which routes matched traffic. Both
download.client_chain and download.bootstrap_url are required. Bootstrap may
be system, an IP-based DNS URL, or a named DNS group. Target and proxy hostnames
use this resolver; failed refresh routing never falls back to direct. Automatic
refresh uses the active generation’s DNS, proxy clients, and selector choices.
All runtime remote sources use compiled rules retained in memory, including when
disk caching is enabled. Reloads reuse sources whose URL and decoding options
are unchanged; runtime compilation never falls back to cache files.
Omit global_settings.rule_cache_dir to disable persistence. Shoes downloads required sources
before starting listeners. Periodic refresh still applies;
failed downloads or rejected candidates leave the active rules unchanged. No
cache, lock, staging, or temporary files are created. Download buffers are released
after validation and optional cache saving; byte-identical refreshes do not recompile the rules.
A cold start requires network access; a remote outage prevents startup when
there is no persistent cache. check and --dry-run remain offline and cannot
validate memory-only remote sources. Local file sources still require path.
global_settings.rule_cache_dir enables best-effort persistence in one private managed directory
across all config files. Valid downloads remain usable even when saving fails.
Cache-save failures are logged; the next successful download retries saving,
including byte-identical downloads. Until a save succeeds, a restart may restore
older cached rules or require network access.
Use persistent caching on routers that must restart while the origin is unavailable.
Its value is relative to the declaring file and fixed for the process lifetime:
adding, changing, or removing it requires a restart, even without remote sources.
Remote path is optional: when omitted, Shoes generates a stable filename from
the URL and decoding options. Explicit remote paths require rule_cache_dir and
must be direct children of that directory. Configuration, local
rule sources (including aliases), PEMs, sockets, and other state must be outside
it. ACME state directories cannot contain or be contained by the cache directory.
On Linux, /run/shoes is reserved for managed-TUN ownership and recovery files;
use a separate cache directory, such as /run/shoes/rule-cache, instead.
The directory must be owned by the running user and not group- or world-writable
(for example, create it with install -d -m 700 cache). Cache files must be owned,
regular, singly linked, not group- or world-writable, and have no setuid, setgid,
or sticky mode bits. Symlinks in any path component
and .. traversal are rejected. owner.lock, .shoes-update.lock, and
.shoes-update.tmp sidecars are reserved. The daemon holds an exclusive directory
lease; another daemon cannot use the same directory concurrently.
Replacing the directory or owner.lock invalidates the lease; subsequent runtime
cache reads and writes are rejected until restart, without acquiring a replacement
lease. Retained rules, downloads, and configuration reloads can continue in memory.
Identical declarations sharing a cache are deduplicated;
conflicting declarations are rejected. Local-only file sources are unchanged.
format remains optional: explicit override, recognized SRS/MRS signature, URL
pathname extension (ignoring query), then cache-path extension. All local decoders
are supported; DAT still needs explicit format/code, and Mihomo YAML/text needs
behavior. Invalid decoding never retries a different format.
update_interval defaults to 24h; use a positive integer with s, m, h, or
d, from 1s through 365d. Daemon startup restores valid cached sources into
memory. Missing, unreadable, invalid, or empty cached sources are fetched through
their configured download routes before preparing candidate listeners. Unsafe
files are never read or replaced; a fresh download may still run in memory.
Initial directory creation and exclusive lease acquisition must succeed when
rule_cache_dir is configured. Reloads prefer retained rules, so removing or editing
a cache does not force a refetch. Changing a retained source’s URL or decoding
options fetches a new source instead of reusing its old cache.
check and --dry-run stay strictly offline: no fetching, directory
creation, leases, or installation. They fail for missing or invalid caches; run
the daemon to populate missing files.
Bootstrap downloads support built-in direct, inline TCP proxy chains and
pools, and normal outbound TLS settings with inline, named, or file-backed PEMs.
Its bootstrap_url must be system or a literal-IP udp://, tcp://, tls://,
or https:// DNS URL. Target and proxy hostnames use that resolver. Every pool
member must be supported. User-defined named client groups (including ordinary
round-robin groups), selectors, URL-test groups, named DNS groups, WireGuard,
QUIC proxy transports, and H3 DNS are unsupported for bootstrap. A DNS group
named system is still a named group, not the built-in resolver. No failed or
unsupported route falls back to direct TCP or system DNS. Literal bootstrap DNS
URLs keep their existing direct routing semantics; OS VPN/TUN routing still applies.
These restrictions apply whenever a source must be downloaded during startup or reload, including every memory-only source on a cold start. With usable caches or retained in-memory sources, preparation accepts the normal refresh routes, including dynamic client and named DNS groups. Populate such caches externally, or bootstrap with a supported route before switching to a runtime-dependent route. Bootstrap must not depend on this instance’s own listeners; indirect dependencies can fail at the download deadline. Candidate and retained Fake-IP ranges remain excluded from download destinations, proxy hops, and bootstrap DNS.
When configured, startup creates the final cache directory if missing, then acquires its lease.
The parent directory must already exist. Creation respects the process umask and
never repairs permissions. If owner access is missing, create the directory with
install -d -m 700 cache before starting. Reload reuses the lease and does not
recreate a removed root.
Declarations and all required bootstrap routes are preflighted before fetching. Sources are downloaded and decoder-validated serially, retained in memory, and optionally saved to disk, then the complete configuration is compiled normally. A failed startup exits before serving; a failed reload retains the active generation. Earlier completed saves remain if a later download, configuration check, or activation fails. This is a per-file repair, not a multi-file disk transaction or a promise of activation.
The process refreshes due sources serially, with 10% jitter and capped exponential
failure backoff. Downloaded sources pass Shoes’ actual decoders, compilation budgets,
and validation against the active configuration snapshot before replacing retained
rules. Persistent and nonpersistent sources use this same validation path.
The initial due time is based on a restored cache’s mtime or the initial download
time; future mtimes are treated as due.
Successful check times are remembered only in memory. On a cold start, an explicit
cache path does not record the URL it came from; use a new cache name when changing
the URL between process lifetimes if immediate replacement is required.
Download or validation failures retain the last-good rules and cache.
Identical downloads reuse compiled rules and do not reload; identical cached bytes
preserve inode/mtime. Each file replacement is atomic, not a multi-file transaction.
A post-rename directory-sync failure reports uncertain durability without rejecting
the update. New flows use the reloaded rules; existing flows retain
their previous configuration. Reload/removal/shutdown cancels in-flight refreshes.
--no-reload disables filesystem-triggered reloads, not configured remote refresh.
Managed caches are excluded from generic filesystem watching. An automatic changed
source requests one immediate reload; external cache edits are read only after restart,
not on SIGHUP. Use local file sources for externally managed live rule updates.
Reload reads the configuration files, including any unrelated edits. If activation
fails, the validated replacement remains available in memory while old rules remain
active. Correct the configuration and reload explicitly; a later byte-identical fetch does not retry
activation. Atomic installation does not provide a disk/activation or power-loss
transaction.
Bulk file reads, writes, and synchronization run off the forwarding executor.
Filesystem work already submitted may finish after cancellation, holding its
update lock until cleanup; it cannot publish an abandoned pre-rename update.
The final ownership check and atomic rename remain one synchronous publication step.
HTTPS verifies certificates. An explicit http:// URL permits cleartext. Redirects,
URL credentials/fragments, HTTP content compression, and trailers are rejected.
Chunked and close-delimited bodies are accepted within the same limits: 64 MiB
download, 64 headers/32 KiB, 10-second connect/DNS/TLS and header/body-idle deadlines,
and a 120-second total download deadline. Decoder-specific decompression limits
still apply. A truncated close-delimited cleartext response can be indistinguishable
from a shorter valid rule list; prefer HTTPS. Logs report
actual updated/unchanged/failed results without URL query values or downloaded data.
Remote refresh currently requires the Unix process runtime. FFI startup and WireGuard-dependent download/bootstrap routes reject it explicitly. It does not add proxy subscriptions, dynamic provider groups, or provider API endpoints.