Before 795d1c289, nested rule-set evaluation reused the parent rule
match cache. In practice, this meant these fields leaked across nested
evaluation:
- SourceAddressMatch
- SourcePortMatch
- DestinationAddressMatch
- DestinationPortMatch
- DidMatch
That leak had two opposite effects.
First, it made included rule-sets partially behave like the docs'
"merged" semantics. For example, if an outer route rule had:
rule_set = ["geosite-additional-!cn"]
ip_cidr = 104.26.10.0/24
and the inline rule-set matched `domain_suffix = speedtest.net`, the
inner match could set `DestinationAddressMatch = true` and the outer
rule would then pass its destination-address group check. This is why
some `rule_set + ip_cidr` combinations used to work.
But the same leak also polluted sibling rules and sibling rule-sets.
A branch could partially match one group, then fail later, and still
leave that group cache set for the next branch. This broke cases such
as gh-3485: with `rule_set = [test1, test2]`, `test1` could touch
destination-address cache before an AdGuard `@@` exclusion made the
whole branch fail, and `test2` would then run against dirty state.
795d1c289 fixed that by cloning metadata for nested rule-set/rule
evaluation and resetting the rule match cache for each branch. That
stopped sibling pollution, but it also removed the only mechanism by
which a successful nested branch could affect the parent rule's grouped
matching state.
As a result, nested rule-sets became pure boolean sub-items against the
outer rule. The previous example stopped working: the inner
`domain_suffix = speedtest.net` still matched, but the outer rule no
longer observed any destination-address-group success, so it fell
through to `final`.
This change makes the semantics explicit instead of relying on cache
side effects:
- `rule_set: ["a", "b"]` is OR
- rules inside one rule-set are OR
- each nested branch is evaluated in isolation
- failed branches contribute no grouped match state
- a successful branch contributes its grouped match state back to the
parent rule
- grouped state from different rule-sets must not be combined together
to satisfy one outer rule
In other words, rule-sets now behave as "OR branches whose successful
group matches merge into the outer rule", which matches the documented
intent without reintroducing cross-branch cache leakage.
PreMatch and full match phases each created a fresh InboundContext,
causing process search (expensive OS syscalls) to run twice per
connection. Use a freelru ShardedLRU cache with 200ms TTL to serve
the second lookup from cache.
Add fpm-based Alpine APK packaging alongside existing DEB/RPM/Pacman
packages. Alpine APKs use `linux` in the filename to distinguish from
OpenWrt APKs which use the `openwrt` prefix.
CCM: Fix 1M context detection - use prefix match for versioned
beta strings (e.g. "context-1m-2025-08-07") and include cache
tokens in the 200K threshold check per Anthropic billing docs.
OCM: Add GPT-5.4 family pricing (standard/priority/flex) with
extended context (>272K) premium pricing support. Add context
window tracking to usage combinations, mirroring CCM's pattern.
Update normalizeGPT5Model defaults to latest known models.
Support the OpenAI Responses WebSocket API (`wss://.../v1/responses`)
for bidirectional frame proxying with usage tracking.
Fix Codex CLI client config examples to use profiles and correct flags.
Update openai-go v3.24.0 → v3.26.0.
When clients (e.g. Node.js Anthropic SDK) explicitly set Accept-Encoding: gzip,
Go's http.Transport does not transparently decompress the response body, because
it only does so when it added the header itself. This causes CCM's json.Unmarshal
to receive raw gzip bytes, silently failing to parse usage data and leaving the
usage counter unchanged.
Fix: remove Accept-Encoding from the outgoing proxy request. Transport adds it
automatically and transparently decompresses response.Body before CCM reads it.
Wire compression (CCM→Anthropic) is preserved — Transport still negotiates gzip.
Only CCM→localhost path is affected; compression on loopback has no practical
benefit.