Skip to content

chore: release v2.7.0#34

Merged
kastov merged 58 commits intomainfrom
dev
Mar 28, 2026
Merged

chore: release v2.7.0#34
kastov merged 58 commits intomainfrom
dev

Conversation

@kastov
Copy link
Copy Markdown
Collaborator

@kastov kastov commented Mar 27, 2026

No description provided.

…nt blocker state management and API config generation
kastov added 25 commits March 10, 2026 19:25
…api to version 0.4.1 in package.json and package-lock.json
…age-lock.json; expand default ignored IPs set
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 27, 2026

Greptile Summary

This release (v2.7.0) introduces a plugin subsystem (torrent-blocker, ingress/egress nftables filters, connection-drop), real-time network stats polling, new protocol support (shadowsocks22, hysteria), and a live webhook feedback loop from xray. Two P1 bugs were found that require fixes before merging.

Confidence Score: 3/5

Not safe to merge as-is: two P1 defects can leave xray routing in an inconsistent state and allow loopback IPs to bypass the torrent-blocker ignore list.

Both P1 findings are present-defect correctness issues on the primary new feature path (torrent-blocker plugin). The CIDR bug silently misclassifies loopback IPs; the restart-guard bug can leave a live xray process with a dangling routing rule referencing a removed outbound.

src/common/constants/default-ignored-ips.ts and src/modules/_plugin/plugin.service.ts require fixes before merging.

Important Files Changed

Filename Overview
src/modules/_plugin/plugin.service.ts New plugin orchestration service; contains a P1 logic bug where the guard deciding hot-remove outbound vs. full xray restart inspects the new (disabled) config rather than the old running state.
src/common/constants/default-ignored-ips.ts New constant file; contains CIDR strings ('0.0.0.0/0', '127.0.0.0/8') in a Set queried with exact-match has(), making those entries permanently ineffective.
src/modules/_plugin/services/nft.service.ts New nftables management service; correctly checks capability before initialising, handles lifecycle cleanup, and delegates block/unblock operations through the event bus.
src/modules/_plugin/events/xray-webhook/xray-webhook.handler.ts New webhook event handler for the torrent-blocker; validates webhook payload via Zod, extracts and validates the IP, checks ignore lists before blocking, and stores reports.
src/modules/handler/handler.service.ts Adds shadowsocks22 and hysteria protocol support, refactors destroyConnections to event-bus dispatch, and extracts removeOutbound helper.
src/modules/network-stats/network-stats.service.ts New polling service reading /proc/net/dev every 1s to compute per-interface byte rates; gracefully disables on non-Linux and cleans up timer on destroy.
src/common/utils/generate-api-config.ts Extends xray config generation to inject torrent-blocker outbound, bittorrent routing rule with webhook URL, and per-rule webhook entries when includeRuleTags is populated.
src/modules/stats/stats.service.ts Adds getUsersIpList (bulk IP list via pMap concurrency 50), enriches getSystemStats with network and plugin data, and improves getUserIpList to include lastSeen timestamps.
src/modules/xray-core/xray.service.ts Migrates system stats from one-time async fetch to synchronous os-module calls, adds torrent-blocker state injection into xray config, and adds plugin cleanup on stopXray.
src/modules/internal/internal.controller.ts Adds POST /internal/webhook endpoint that publishes XrayWebhookEvent; reachable only via the internal Unix-socket server and carries a token in the URL.

Sequence Diagram

sequenceDiagram
    participant Panel as Remnawave Panel
    participant PC as PluginController
    participant PS as PluginService
    participant PSS as PluginStateService
    participant NFT as NftService
    participant XS as XrayService
    participant XC as XrayCore
    participant IC as InternalController
    participant XWH as XrayWebhookHandler

    Panel->>PC: POST /plugin/sync (plugin config)
    PC->>PS: sync(body)
    PS->>PSS: capture old torrentBlocker state
    PS->>PSS: resetState() / cleanUpActivePlugin()
    PS->>NFT: recreateTables()
    PS->>PSS: syncTorrentBlocker / syncConnectionDrop
    PS->>NFT: syncIngressFilter / syncEgressFilter
    alt torrentBlocker disabled and no old includeRuleTags
        PS->>XS: RemoveOutboundCommand(tag)
        XS->>XC: removeOutbound(RW_TB_OUTBOUND_BLOCK)
    else config changed
        PS->>XS: StopXrayCommand(withOnlineCheck)
        XS->>XC: kill xray processes
    end
    Panel->>XS: StartXrayCommand
    XS->>PSS: GetTorrentBlockerStateQuery
    XS->>XS: generateApiConfig (inject TB outbound + routing rule)
    XS->>XC: start xray with full config
    XC-->>IC: POST /internal/webhook (bittorrent detected)
    IC->>XWH: publish XrayWebhookEvent
    XWH->>PSS: isIpIgnored / isUserIgnored?
    alt IP not ignored
        XWH->>NFT: blockIp(ip, duration)
        NFT->>XC: nftables add address
        XWH->>PSS: addReport(...)
    end
Loading

Reviews (1): Last reviewed commit: "chore: update XRAY_CORE_VERSION to v26.3..." | Re-trigger Greptile

Comment on lines +105 to +108
if (wasEnabled && !nowEnabled && !pluginData.torrentBlocker?.includeRuleTags) {
await this.commandBus.execute(
new RemoveOutboundCommand(XRAY_TORRENT_BLOCKER_OUTBOUND_TAG),
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Incorrect guard for hot-remove vs. full xray restart

The condition !pluginData.torrentBlocker?.includeRuleTags inspects the new config object (which has torrent-blocker disabled, so pluginData.torrentBlocker is typically undefined). This means the expression is almost always !undefined → true, causing the first branch to be taken regardless of what the old running config contained.

Scenario that breaks xray:

  1. Old config: torrent-blocker enabled with includeRuleTags: ['my-rule'] → xray's routing rules contain webhook entries for my-rule.
  2. New config: torrent-blocker disabled.
  3. Guard evaluates !undefined === true → only RemoveOutboundCommand is dispatched.
  4. Result: xray's routing rules still reference the removed outbound RW_TB_OUTBOUND_BLOCK and the orphaned webhook entries remain active.

The gate should check the previously captured currentTorrentBlockerIncludeRuleTags (which reflects what xray is actually running with), not the incoming (disabled) plugin config:

Suggested change
if (wasEnabled && !nowEnabled && !pluginData.torrentBlocker?.includeRuleTags) {
await this.commandBus.execute(
new RemoveOutboundCommand(XRAY_TORRENT_BLOCKER_OUTBOUND_TAG),
);
if (wasEnabled && !nowEnabled && currentTorrentBlockerIncludeRuleTags.size === 0) {

This ensures a restart (rather than a hot-remove) is triggered whenever the old config had added webhook rules to xray's routing table.

@kastov kastov merged commit 45f66ea into main Mar 28, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant