Skip to content

katana

katana is an XrayR-compatible proxy node agent built on the Etemenanki kernel. It speaks two of the panel APIs XrayR speaks, UniProxy and SSPanel’s mod_mu, and its [node.api] and [node.controller] keys are a snake_case subset of XrayR’s ApiConfig and ControllerConfig (host for ApiHost, node_id for NodeID, update_periodic for UpdatePeriodic, and so on). You point it at a panel (Xboard, V2board or SSPanel) and give it a node ID. katana asks the panel what the node looks like and who may use it, serves that node, holds every user to their speed limit and to the panel’s audit rules, and reports each user’s traffic back to the panel.

This page is the map. It shows the moving parts, lists the panels and protocols katana supports, draws the line between what the panel decides and what your configuration file decides, and names the features katana leaves out on purpose. Read it before you write your first [[node]]; the rest of the katana section goes into each part in detail.

One katana process serves one or more nodes. Each [[node]] in the configuration file gets its own node manager, which keeps its own connection to its panel, its own listener and its own routing table. All nodes share one pool of outbounds and one DNS resolver.

flowchart TB
  Panel["Panel: UniProxy or mod_mu"]
  Client["Clients"]
  Dest["Destinations"]
  subgraph K["katana"]
    NM["Node manager"]
    L["Listener: TCP, TLS, WebSocket, gRPC or QUIC"]
    Core["Protocol core"]
    Adm["User admission"]
    R["Router"]
    A["Audit rules"]
    M["Meter: speed limit and byte counts"]
    Pool["Outbound pool"]
  end
  Panel -- "node info, users, audit rules" --> NM
  NM -- "traffic, audit hits (SSPanel)" --> Panel
  NM -. "builds, swaps user table" .-> L
  Client --> L --> Core --> Adm --> R --> A --> M --> Pool --> Dest
  M -. "bytes per user" .-> NM

Two loops run side by side.

  • The control loop. Every update_periodic seconds (default 60), the node manager fetches the node’s settings and user list from the panel, applies any change, refreshes the audit rules, and reports the traffic counted since the last report.
  • The data path. A client connects to the node’s listener. The listener unwraps the transport (TLS, WebSocket, gRPC, or QUIC for Hysteria 2) and hands the bytes to the protocol core, which authenticates the user against the table the panel supplied. Every flow the client opens, whether a TCP request, a mux sub-flow or a UDP association, then passes these stages. UDP packets are routed and audited one at a time.
    1. Admission checks that the user is still in the node’s current user list. A user the panel has removed is refused, and their open connections are ended.
    2. The router picks an outbound from the node’s [node.route] rules. A flow routed to block is refused.
    3. The audit rules refuse the flow if its destination host (domain or IP, without the port) matches one of them, and record the hit.
    4. The meter wraps the outbound connection. It counts the user’s upload and download bytes and paces them with the user’s speed limit. All of one user’s flows on the node share one limit.
    5. The outbound connects to the destination: directly, or through an upstream proxy or WireGuard tunnel from [[outbound]].

katana dials the destination before it answers a client’s TCP request: VMess and VLESS send their response header only once the destination is connected. If the dial fails, or the router or the audit rules refuse the flow, the request ends instead; inside a mux connection, only that sub-flow ends. Trojan and Shadowsocks send no reply, so for them a refused request closes the connection.

This is a complete configuration for one node on an Xboard panel. Notice what is missing: no port, no protocol settings, no users. The panel supplies all of those.

/etc/katana/config.toml
# One node, served for an Xboard panel. Port, transport, TLS on/off and
# users all come from the panel; this file says how to reach it.
[[node]]
panel_type = "NewV2board" # Xboard and V2board (UniProxy API)
[node.api]
host = "https://panel.example.com"
node_id = 1
key = "replace-with-the-panel-key"
node_type = "V2ray" # the panel node is a VMess node
[node.controller]
listen_ip = "0.0.0.0"
update_periodic = 60 # seconds between panel polls and reports
[node.controller.cert] # used only if the panel turns TLS on
mode = "file"
cert_file = "/etc/katana/fullchain.pem"
key_file = "/etc/katana/privkey.pem"
[node.route]
default = "direct"

katana --test -c /etc/katana/config.toml checks the file and prints Configuration OK. It builds the outbounds, each node’s panel client and routing table, and checks the local [node.hysteria] settings of Hysteria 2 nodes, but it binds no port and does not contact the panel. What the panel will send is only checked when the node starts. The configuration file page covers every key.

panel_type selects the panel API. katana compares it case-insensitively, so NewV2board, newv2board and NEWV2BOARD are the same.

Panel panel_type API katana calls Node types katana accepts
Xboard, V2board NewV2board or V2board UniProxy: GET /api/v1/server/UniProxy/config, GET …/user, POST …/push V2ray (VMess or VLESS), Trojan, Shadowsocks, Hysteria 2
SSPanel SSpanel mod_mu: /mod_mu/nodes/{node_id}/info, /mod_mu/users, /mod_mu/users/traffic, /mod_mu/func/detect_rules, /mod_mu/users/detectlog V2ray (VMess or VLESS), Trojan, Hysteria 2
  • V2board is an alias of NewV2board; both use the UniProxy client.
  • Any other value makes --test fail with configuration error: unknown panel_type "xboard" (the value is shown lowercased). At startup katana logs the same error, skips that node and starts the others; it exits only when no node can be started.
  • Both clients send If-None-Match with the last ETag the panel returned. When the panel answers 304 Not Modified, katana keeps the node settings, user list or rules it already has.
  • SSPanel refuses Shadowsocks nodes (sspanel: Shadowsocks node type is not supported) when the node starts. An SSPanel Hysteria 2 node must come from the panel’s custom_config, because the legacy server string cannot describe one, or be described locally with [node.hysteria].port.

The per-panel pages list every field katana reads: Xboard and V2board and SSPanel.

[node.api].node_type tells katana what kind of node the panel describes. It is compared case-insensitively. Any other value fails --test with unknown node_type "…", and at startup that node is skipped with the same error.

node_type Protocol served Transports TLS UDP
V2ray (also vmess, vless) VMess with AEAD headers, or VLESS without flow TCP, WebSocket, gRPC Optional, as the panel says Carried inside the connection, including mux.cool and XUDP
Trojan Trojan; each user’s password is their UUID UniProxy: TCP. SSPanel: TCP, WebSocket or gRPC from custom_config, TCP or gRPC from the legacy server string Always Carried inside the connection
Shadowsocks AEAD (SIP004) or Shadowsocks 2022 multi-user (SIP022), chosen by the panel’s cipher TCP None Not served: katana binds no UDP socket for Shadowsocks
Hysteria2 (also hysteria, hy2) Hysteria 2 over QUIC QUIC on UDP Always, inside QUIC When [node.hysteria].udp = true
  • A V2ray node serves VLESS instead of VMess when [node.api].enable_vless = true, or when an SSPanel custom_config has enable_vless = "1".
  • VMess, VLESS and Trojan nodes accept mux.cool from clients; Shadowsocks nodes do not. Each mux sub-flow is routed, audited and metered on its own, and all of them draw on the user’s single speed limit.
  • Transport names from the panel are read case-insensitively: tcp or raw, ws or websocket, grpc or gun. An empty value means TCP.
  • A TLS or Hysteria 2 node needs [node.controller.cert].mode = "file" with a certificate and key in PEM files.

Protocols describes each protocol’s settings and client requirements, and Hysteria 2 covers the local [node.hysteria] table.

What the panel decides, and what you decide

Section titled “What the panel decides, and what you decide”

katana splits a node’s settings in two. The panel owns everything clients must agree on and everything that changes per user. Your configuration file owns everything about this machine and this operator.

Setting Comes from Details
Listening port Panel UniProxy server_port; SSPanel offset_port_node in custom_config, or the legacy server string. A locally described Hysteria 2 node uses [node.hysteria].port
Transport Panel network of V2ray nodes and SSPanel Trojan nodes: TCP, WebSocket or gRPC. UniProxy Trojan nodes are always TCP
WebSocket path and Host, gRPC service name Panel UniProxy networkSettings for VMess and network_settings for VLESS; SSPanel path, host and servicename
TLS on or off Panel V2ray nodes: UniProxy tls = 1, SSPanel security = "tls" or "xtls", or tls in the legacy server string. Trojan and Hysteria 2 always use TLS
Shadowsocks cipher and server key Panel UniProxy cipher and server_key
Users Panel ID and UUID of every user allowed on the node
Per-user speed limit Panel Each user’s speed_limit (UniProxy) or node_speedlimit (SSPanel), in Mbps
Node speed limit Panel SSPanel only: the node’s node_speedlimit
Audit rules Panel, plus a local file UniProxy: the node’s routes with action = "block". SSPanel: /mod_mu/func/detect_rules
Hysteria 2 obfuscation Panel obfs and obfs-password, unless you describe the node locally with [node.hysteria].port
Panel connection Local panel_type, and host, node_id, key, node_type and timeout (default 5 seconds) in [node.api]
VMess or VLESS Local [node.api].enable_vless; SSPanel can also turn VLESS on from custom_config
Certificates Local [node.controller.cert]. The panel’s tls_settings and certificate settings are not read
Listen address Local [node.controller].listen_ip, default "0.0.0.0"
Poll and report interval Local [node.controller].update_periodic, default 60 seconds. The panel’s push and pull intervals are not read
Outbounds Local [[outbound]], shared by all nodes
Routing Local [node.route], one table per node
DNS Local [dns], shared by all nodes
Speed-limit override Local [node.api].speed_limit in Mbps. Above 0, it replaces every user’s panel limit, and on SSPanel the node limit too
Local audit rules Local [node.api].rule_list_path, one regular expression per line; blank lines and lines starting with # are skipped. katana reads the file together with the panel’s rules, so disable_get_rule = true turns local rules off too
Sniffing, rule fetching, traffic upload Local disable_sniffing, disable_get_rule, disable_upload_traffic in [node.controller]
Hysteria 2 listener Local [node.hysteria]: credential, udp, udp_idle_timeout and masquerade, plus port, obfs and obfs_password for a locally described node

When a user has both a node limit and a user limit, the smaller non-zero one applies. Speed limits are in megabits per second; katana converts 1 Mbps to 125 000 bytes per second. Speed limits explains how the limit is enforced.

  1. Start. The node manager fetches the node’s settings and its user list, builds the listener and binds it. A node whose user list is empty binds nothing until users appear. If any part of the start fails, katana tries again, as the note after this list describes. When the listener is up, katana logs:

    2026-09-24T20:22:05.118204Z INFO katana::manager::node: node 1: listening on 0.0.0.0:443
  2. Poll. One full update_periodic period after the node is up, and every period after that, the node manager runs one cycle: fetch the node settings, fetch the users, apply changes, refresh the audit rules, report traffic, report audit hits. A failed fetch keeps the last settings and users that worked. Node settings with port 0 keep the last node settings, while the users from that poll still apply. A failed traffic report keeps the counts for the next cycle. An accepted edit to the configuration file that affects the node’s panel client or its listener runs one cycle at once, without waiting for the timer.

  3. Change. What a change costs depends on what changed:

    • users added or removed, or a speed limit changed: katana swaps the user table in place. The connections of users who stay continue, and a new speed limit applies to the flows opened after the change; a removed user’s connections end. If the list becomes empty, katana closes the listener.

    • a new port, transport, path, TLS setting, cipher or obfuscation: katana rebuilds the listener, which drops every connection on the node.

    • an edit to katana’s own configuration file: katana applies it without a restart.

      • A new listen address, certificate, enable_vless, disable_sniffing, [node.hysteria] setting, routing rule or outbound rebuilds the listener.
      • Other [node.api] edits, such as speed_limit, rule_list_path or timeout, apply live through a new panel client, which reads the node settings and users again at once. They drop connections only if the answer changes a panel setting that rebuilds the listener, such as the port or transport.
      • A new panel_type, host, node_id or key names a different panel node, so katana stops the node and starts it again. On Xboard and V2board the same happens when a node_type edit changes the type katana asks for (a change of letter case does not), or when enable_vless changes on a V2ray or Vmess node, because UniProxy finds a node by the type it is asked for as well as its ID.
      • If the edited file’s outbounds or any of its nodes do not build (an unknown node_type, for example), katana refuses the whole reload and every node keeps running as it was. A node edit whose routing rules do not build is refused for that node, which keeps its running settings.
      • A change to [dns] alone is not applied.

      Hot reload lists what each edit does.

  4. Stop. On SIGINT or SIGTERM, each node closes its listener, reports its remaining traffic and audit hits once, and exits.

For the features below that a node can ask for, katana refuses the node instead of quietly doing something weaker: the node fails to start, or to rebuild, with an error that names the feature. The caution after the table lists panel settings that katana neither serves nor refuses.

Feature What katana does instead
ACME certificates (cert.mode = "dns", "http" or "tls") Refuses the node: node requests kernel-unsupported feature: ACME cert mode "dns". A Hysteria 2 node reports hysteria2 node requires cert.mode = "file" instead. Obtain and renew certificates yourself and use mode = "file".
REALITY Refuses a node whose panel settings ask for it (UniProxy V2ray tls = 2, SSPanel enable_reality in custom_config): node requests kernel-unsupported feature: REALITY.
XTLS flow Refuses a node with a non-empty flow, from the panel or from [node.api].vless_flow with the legacy SSPanel server string: node requests kernel-unsupported feature: VLESS XTLS flow.
httpupgrade, splithttp and xhttp transports Refuses the node, for example node requests kernel-unsupported feature: httpupgrade transport. Any other unknown transport is refused the same way.
cert.reject_unknown_sni = true Refuses the node: node requests kernel-unsupported feature: cert.reject_unknown_sni.
Online users and alive IPs Not reported. katana never calls UniProxy alive or alivelist, or SSPanel /mod_mu/users/aliveip, so the panel shows no online users for the node.
Device limits Not enforced. [node.api].device_limit is accepted and ignored, and the panel’s per-user device limit is not read.
Outbound source address Not applied. [node.controller].send_ip is accepted and ignored; outbound connections leave from the address the host’s routing table picks.
Node status Not reported. katana never sends UniProxy status or the SSPanel node status, so the panel shows no CPU, memory or disk figures. Xboard still records the node’s check-in on every user-list request.
Audit-hit reports to UniProxy panels The UniProxy API has no endpoint for them. katana still refuses the flows. Only SSPanel receives hits, and only for its own detect_rules; hits on local rule_list_path rules are never reported.
Hysteria 2 Brutal congestion control Not implemented. The panel’s up_mbps and down_mbps are ignored; per-user speed limits come from the meter.
Shadowsocks plugins Not supported. A UniProxy obfs value other than empty, plain or none fails the node with newV2board: shadowsocks obfs "…" is not supported.
Page Read it when you want to…
Quick start Bring up a first node against a panel
Configuration file Look up any key, its type and default
Xboard and V2board Set up the panel side for UniProxy, and see which fields katana reads
SSPanel Set up custom_config or the legacy server string
Nodes Run several nodes in one process, choose ports and listen addresses
Protocols Serve VMess, VLESS, Trojan or Shadowsocks, with TLS certificates
Hysteria 2 Serve a Hysteria 2 node, from the panel or described locally
Outbounds Send traffic through an upstream proxy or a WireGuard tunnel
Routing Write [node.route] rules with domains, CIDRs, ports, GeoIP and GeoSite
Speed limits Understand per-user and per-node limits and the override
Audit rules Block destinations with panel or local rules
Traffic reporting Know when and how traffic reaches the panel
Hot reload Edit the configuration of a running katana
Deployment Install katana as a service
Migrating from XrayR Translate an XrayR configuration
Troubleshooting Read an error message or a node that does not come up