From 6b9ae7049174b44557baf8ad2d55ff4e9792e89b Mon Sep 17 00:00:00 2001 From: shanshanzhong Date: Mon, 27 Jul 2026 00:18:47 -0700 Subject: [PATCH] feat(simnet): add SimNet protocol to admin server form - form-schema: constants/defaults/schemas/types + full ~48 simnet field set - server-form: SimNet field group rendering - node-form: allow simnet protocol type - i18n: en-US / zh-CN simnet labels --- .../public/assets/locales/en-US/servers.json | 48 +- .../public/assets/locales/zh-CN/servers.json | 48 +- apps/admin/src/sections/nodes/node-form.tsx | 3 +- .../sections/servers/form-schema/constants.ts | 8 + .../sections/servers/form-schema/defaults.ts | 60 +++ .../sections/servers/form-schema/schemas.ts | 66 +++ .../src/sections/servers/form-schema/types.ts | 3 +- .../servers/form-schema/useProtocolFields.ts | 497 ++++++++++++++++++ .../src/sections/servers/server-form.tsx | 9 + 9 files changed, 738 insertions(+), 4 deletions(-) diff --git a/apps/admin/public/assets/locales/en-US/servers.json b/apps/admin/public/assets/locales/en-US/servers.json index 57f59e0..1897901 100644 --- a/apps/admin/public/assets/locales/en-US/servers.json +++ b/apps/admin/public/assets/locales/en-US/servers.json @@ -49,8 +49,8 @@ "encryption_rtt": "RTT", "encryption_server_padding": "Server Padding", "encryption_ticket": "Ticket Time", - "expired": "Expired", "expireTime": "Expire Time", + "expired": "Expired", "extra": "Extra", "flow": "Flow", "generate_quantum_resistant_key": "Generate Quantum-Resistant Key", @@ -141,6 +141,52 @@ }, "server_key": "Server Key", "service_name": "Service Name", + "simnet": "SimNet", + "simnet_af_enabled": "Enable Anti-Fingerprint", + "simnet_af_fake_header_injection": "Fake Header Injection", + "simnet_af_handshake_polymorphism": "Handshake Polymorphism", + "simnet_af_magic_mode": "Anti-Fingerprint Magic Mode", + "simnet_af_path_mode": "Anti-Fingerprint Path Mode", + "simnet_af_path_prefix": "Anti-Fingerprint Path Prefix", + "simnet_af_path_prefix_placeholder": "Optional path prefix", + "simnet_af_path_suffix": "Anti-Fingerprint Path Suffix", + "simnet_af_path_suffix_placeholder": "Optional path suffix", + "simnet_af_response_jitter_ms": "Anti-Fingerprint Response Jitter", + "simnet_af_settings_jitter": "Settings Frame Jitter", + "simnet_client_max_concurrent_streams": "Client Concurrent Streams", + "simnet_client_max_streams_per_session": "Client Streams / Session", + "simnet_client_max_udp_sessions": "Client UDP Sessions", + "simnet_client_session_idle_timeout_secs": "Client Session Idle Timeout", + "simnet_egress_block_link_local": "Block Link-Local Egress", + "simnet_egress_block_loopback": "Block Loopback Egress", + "simnet_egress_block_metadata": "Block Metadata Egress", + "simnet_egress_block_private": "Block Private Egress", + "simnet_fallback_enabled": "Enable HTTPS Fallback Reverse Proxy", + "simnet_fallback_host_header": "Fallback Host Header", + "simnet_fallback_host_header_placeholder": "Optional, defaults to target host", + "simnet_fallback_target_host": "Fallback Target Host", + "simnet_fallback_target_host_placeholder": "e.g. www.example.com", + "simnet_fallback_target_port": "Fallback Target Port", + "simnet_fallback_target_scheme": "Fallback Target Scheme", + "simnet_fallback_tls_sni": "Fallback TLS SNI", + "simnet_fallback_tls_sni_placeholder": "Optional, defaults to target host", + "simnet_inbound_max_handler_tasks_per_session": "Handler Tasks / Session", + "simnet_inbound_max_streams_per_session": "Inbound Streams / Session", + "simnet_inbound_max_udp_streams_per_session": "Inbound UDP Streams / Session", + "simnet_initial_window_size": "H2 Initial Window", + "simnet_max_concurrent_streams": "H2 Concurrent Streams", + "simnet_max_frame_size": "H2 Max Frame Size", + "simnet_recv_window": "Receive Window", + "simnet_reverse_enabled": "Enable Local Reverse Proxy", + "simnet_reverse_listen_addr": "Reverse Listen Address", + "simnet_reverse_listen_port": "Reverse Listen Port", + "simnet_reverse_target_host": "Reverse Target Host", + "simnet_reverse_target_port": "Reverse Target Port", + "simnet_send_window": "Send Window", + "simnet_stream_data_channel_capacity": "Stream Data Channel", + "simnet_stream_event_channel_capacity": "Stream Event Channel", + "simnet_target_dial_timeout_ms": "Target Dial Timeout", + "simnet_target_max_concurrent_dials": "Concurrent Target Dials", "sorted_success": "Sorted successfully", "status": "Status", "subscribeId": "Subscribe ID", diff --git a/apps/admin/public/assets/locales/zh-CN/servers.json b/apps/admin/public/assets/locales/zh-CN/servers.json index eac5b5e..e9109bc 100644 --- a/apps/admin/public/assets/locales/zh-CN/servers.json +++ b/apps/admin/public/assets/locales/zh-CN/servers.json @@ -49,8 +49,8 @@ "encryption_rtt": "RTT", "encryption_server_padding": "服务器填充", "encryption_ticket": "票据时间", - "expired": "已过期", "expireTime": "过期时间", + "expired": "已过期", "extra": "额外", "flow": "流控", "generate_quantum_resistant_key": "生成量子抗性密钥", @@ -141,6 +141,52 @@ }, "server_key": "服务器密钥", "service_name": "服务名称", + "simnet": "SimNet", + "simnet_af_enabled": "启用反指纹", + "simnet_af_fake_header_injection": "注入伪装 Header", + "simnet_af_handshake_polymorphism": "握手多态", + "simnet_af_magic_mode": "反指纹 Magic 模式", + "simnet_af_path_mode": "反指纹路径模式", + "simnet_af_path_prefix": "反指纹路径前缀", + "simnet_af_path_prefix_placeholder": "可选路径前缀", + "simnet_af_path_suffix": "反指纹路径后缀", + "simnet_af_path_suffix_placeholder": "可选路径后缀", + "simnet_af_response_jitter_ms": "反指纹响应抖动", + "simnet_af_settings_jitter": "Settings 帧抖动", + "simnet_client_max_concurrent_streams": "客户端并发流上限", + "simnet_client_max_streams_per_session": "客户端每会话流上限", + "simnet_client_max_udp_sessions": "客户端 UDP 会话上限", + "simnet_client_session_idle_timeout_secs": "客户端会话空闲超时", + "simnet_egress_block_link_local": "阻止出口访问链路本地地址", + "simnet_egress_block_loopback": "阻止出口访问 Loopback", + "simnet_egress_block_metadata": "阻止出口访问元数据地址", + "simnet_egress_block_private": "阻止出口访问私网", + "simnet_fallback_enabled": "启用 HTTPS Fallback 反向代理", + "simnet_fallback_host_header": "Fallback Host Header", + "simnet_fallback_host_header_placeholder": "可选,默认使用目标主机", + "simnet_fallback_target_host": "Fallback 目标主机", + "simnet_fallback_target_host_placeholder": "例如 www.example.com", + "simnet_fallback_target_port": "Fallback 目标端口", + "simnet_fallback_target_scheme": "Fallback 目标协议", + "simnet_fallback_tls_sni": "Fallback TLS SNI", + "simnet_fallback_tls_sni_placeholder": "可选,默认使用目标主机", + "simnet_inbound_max_handler_tasks_per_session": "每会话处理任务上限", + "simnet_inbound_max_streams_per_session": "每会话入站流上限", + "simnet_inbound_max_udp_streams_per_session": "每会话入站 UDP 流上限", + "simnet_initial_window_size": "H2 初始窗口", + "simnet_max_concurrent_streams": "H2 并发流上限", + "simnet_max_frame_size": "H2 最大帧大小", + "simnet_recv_window": "接收窗口", + "simnet_reverse_enabled": "启用本地反向代理", + "simnet_reverse_listen_addr": "反代监听地址", + "simnet_reverse_listen_port": "反代监听端口", + "simnet_reverse_target_host": "反代目标主机", + "simnet_reverse_target_port": "反代目标端口", + "simnet_send_window": "发送窗口", + "simnet_stream_data_channel_capacity": "流数据队列容量", + "simnet_stream_event_channel_capacity": "流事件队列容量", + "simnet_target_dial_timeout_ms": "目标拨号超时", + "simnet_target_max_concurrent_dials": "并发目标拨号上限", "sorted_success": "排序成功", "status": "状态", "subscribeId": "订阅 ID", diff --git a/apps/admin/src/sections/nodes/node-form.tsx b/apps/admin/src/sections/nodes/node-form.tsx index a991ffd..67711cd 100644 --- a/apps/admin/src/sections/nodes/node-form.tsx +++ b/apps/admin/src/sections/nodes/node-form.tsx @@ -50,7 +50,8 @@ export type ProtocolName = | "naive" | "http" | "socks" - | "mieru"; + | "mieru" + | "simnet"; const buildSchema = (t: TFunction) => z.object({ diff --git a/apps/admin/src/sections/servers/form-schema/constants.ts b/apps/admin/src/sections/servers/form-schema/constants.ts index 1645f9e..0b06605 100644 --- a/apps/admin/src/sections/servers/form-schema/constants.ts +++ b/apps/admin/src/sections/servers/form-schema/constants.ts @@ -10,6 +10,7 @@ export const protocols = [ "naive", "http", "mieru", + "simnet", ] as const; // Global label map for display; fallback to raw value if missing @@ -69,6 +70,7 @@ export const SECURITY = { anytls: ["none", "tls", "reality"] as const, naive: ["none", "tls"] as const, http: ["none", "tls"] as const, + simnet: ["none", "tls"] as const, } as const; export const FLOWS = { @@ -99,6 +101,12 @@ export const FINGERPRINTS = [ export const CERT_MODES = ["none", "http", "dns", "self"] as const; +// Simnet enums (first version keeps carrier/af modes locked to supported values). +export const SIMNET_CARRIERS = ["h2", "ws", "raw"] as const; +export const SIMNET_AF_PATH_MODES = ["api", "random"] as const; +export const SIMNET_AF_MAGIC_MODES = ["derived", "random"] as const; +export const SIMNET_FALLBACK_SCHEMES = ["https", "http"] as const; + export const multiplexLevels = ["none", "low", "middle", "high"] as const; export function getLabel(value: string): string { diff --git a/apps/admin/src/sections/servers/form-schema/defaults.ts b/apps/admin/src/sections/servers/form-schema/defaults.ts index 6b6f93a..d66e371 100644 --- a/apps/admin/src/sections/servers/form-schema/defaults.ts +++ b/apps/admin/src/sections/servers/form-schema/defaults.ts @@ -171,6 +171,66 @@ export function getProtocolDefaultConfig(proto: ProtocolType) { multiplex: "none", transport: "tcp", } as any; + case "simnet": + return { + type: "simnet", + enable: false, + port: 443, + simnet_psk: null, + simnet_key_id: null, + simnet_ticket_id: null, + simnet_path: null, + simnet_carrier: "h2", + security: "tls", + sni: null, + allow_insecure: false, + cert_mode: "none", + cert_dns_provider: null, + cert_dns_env: null, + simnet_af_enabled: false, + simnet_af_path_mode: "api", + simnet_af_path_prefix: null, + simnet_af_path_suffix: null, + simnet_af_magic_mode: "derived", + simnet_af_response_jitter_ms: 50, + simnet_af_handshake_polymorphism: false, + simnet_af_settings_jitter: false, + simnet_af_fake_header_injection: false, + simnet_reverse_enabled: false, + simnet_reverse_listen_addr: "127.0.0.1", + simnet_reverse_listen_port: null, + simnet_reverse_target_host: "127.0.0.1", + simnet_reverse_target_port: null, + simnet_fallback_enabled: false, + simnet_fallback_target_scheme: "https", + simnet_fallback_target_host: null, + simnet_fallback_target_port: null, + simnet_fallback_host_header: null, + simnet_fallback_tls_sni: null, + simnet_inbound_max_streams_per_session: 128, + simnet_inbound_max_udp_streams_per_session: 64, + simnet_inbound_max_handler_tasks_per_session: 128, + simnet_stream_event_channel_capacity: 256, + simnet_stream_data_channel_capacity: 128, + simnet_target_dial_timeout_ms: 12_000, + simnet_target_max_concurrent_dials: 256, + simnet_egress_block_loopback: false, + simnet_egress_block_private: false, + simnet_egress_block_link_local: false, + simnet_egress_block_metadata: false, + simnet_send_window: 4_194_304, + simnet_recv_window: 4_194_304, + simnet_max_concurrent_streams: 100, + simnet_initial_window_size: 65_535, + simnet_max_frame_size: 16_384, + simnet_client_max_concurrent_streams: 32, + simnet_client_max_streams_per_session: 512, + simnet_client_session_idle_timeout_secs: 90, + simnet_client_max_udp_sessions: 64, + up_mbps: null, + down_mbps: null, + ratio: 1, + } as any; case "anytls": return { type: "anytls", diff --git a/apps/admin/src/sections/servers/form-schema/schemas.ts b/apps/admin/src/sections/servers/form-schema/schemas.ts index f54515a..e83e60e 100644 --- a/apps/admin/src/sections/servers/form-schema/schemas.ts +++ b/apps/admin/src/sections/servers/form-schema/schemas.ts @@ -7,6 +7,10 @@ import { FLOWS, multiplexLevels, SECURITY, + SIMNET_AF_MAGIC_MODES, + SIMNET_AF_PATH_MODES, + SIMNET_CARRIERS, + SIMNET_FALLBACK_SCHEMES, SS_CIPHERS, TRANSPORTS, TUIC_CONGESTION, @@ -18,6 +22,7 @@ const nullableString = z.string().nullish(); const nullableBool = z.boolean().nullish(); const nullablePort = z.number().int().min(0).max(65_535).nullish(); const nullableRatio = z.number().min(0).nullish(); +const nullableNonNegativeInt = z.number().int().min(0).nullish(); const ss = z.object({ ratio: nullableRatio, @@ -207,6 +212,66 @@ const mieru = z.object({ transport: z.enum(TRANSPORTS.mieru).nullish(), }); +const simnet = z.object({ + ratio: nullableRatio, + type: z.literal("simnet"), + enable: nullableBool, + port: nullablePort, + simnet_psk: nullableString, + simnet_key_id: z.number().int().nullish(), + simnet_ticket_id: nullableString, + simnet_path: nullableString, + simnet_carrier: z.enum(SIMNET_CARRIERS).nullish(), + security: z.enum(["none", "tls"] as const).nullish(), + sni: nullableString, + allow_insecure: nullableBool, + cert_mode: z.enum(CERT_MODES).nullish(), + cert_dns_provider: nullableString, + cert_dns_env: nullableString, + simnet_af_enabled: nullableBool, + simnet_af_path_mode: z.enum(SIMNET_AF_PATH_MODES).nullish(), + simnet_af_path_prefix: nullableString, + simnet_af_path_suffix: nullableString, + simnet_af_magic_mode: z.enum(SIMNET_AF_MAGIC_MODES).nullish(), + simnet_af_response_jitter_ms: z.number().int().min(0).nullish(), + simnet_af_handshake_polymorphism: nullableBool, + simnet_af_settings_jitter: nullableBool, + simnet_af_fake_header_injection: nullableBool, + simnet_reverse_enabled: nullableBool, + simnet_reverse_listen_addr: nullableString, + simnet_reverse_listen_port: nullablePort, + simnet_reverse_target_host: nullableString, + simnet_reverse_target_port: nullablePort, + simnet_fallback_enabled: nullableBool, + simnet_fallback_target_scheme: z.enum(SIMNET_FALLBACK_SCHEMES).nullish(), + simnet_fallback_target_host: nullableString, + simnet_fallback_target_port: nullablePort, + simnet_fallback_host_header: nullableString, + simnet_fallback_tls_sni: nullableString, + simnet_inbound_max_streams_per_session: nullableNonNegativeInt, + simnet_inbound_max_udp_streams_per_session: nullableNonNegativeInt, + simnet_inbound_max_handler_tasks_per_session: nullableNonNegativeInt, + simnet_stream_event_channel_capacity: nullableNonNegativeInt, + simnet_stream_data_channel_capacity: nullableNonNegativeInt, + simnet_target_dial_timeout_ms: nullableNonNegativeInt, + simnet_target_max_concurrent_dials: nullableNonNegativeInt, + simnet_egress_block_loopback: nullableBool, + simnet_egress_block_private: nullableBool, + simnet_egress_block_link_local: nullableBool, + simnet_egress_block_metadata: nullableBool, + simnet_send_window: nullableNonNegativeInt, + simnet_recv_window: nullableNonNegativeInt, + simnet_max_concurrent_streams: nullableNonNegativeInt, + simnet_initial_window_size: nullableNonNegativeInt, + simnet_max_frame_size: nullableNonNegativeInt, + simnet_client_max_concurrent_streams: nullableNonNegativeInt, + simnet_client_max_streams_per_session: nullableNonNegativeInt, + simnet_client_session_idle_timeout_secs: nullableNonNegativeInt, + simnet_client_max_udp_sessions: nullableNonNegativeInt, + up_mbps: z.number().nullish(), + down_mbps: z.number().nullish(), +}); + export const protocolApiScheme = z.discriminatedUnion("type", [ ss, vmess, @@ -219,6 +284,7 @@ export const protocolApiScheme = z.discriminatedUnion("type", [ naive, http, mieru, + simnet, ]); export const formSchema = z.object({ diff --git a/apps/admin/src/sections/servers/form-schema/types.ts b/apps/admin/src/sections/servers/form-schema/types.ts index 2c7b305..fdc1a7f 100644 --- a/apps/admin/src/sections/servers/form-schema/types.ts +++ b/apps/admin/src/sections/servers/form-schema/types.ts @@ -32,7 +32,8 @@ export type FieldConfig = { | "security" | "reality" | "obfs" - | "encryption"; + | "encryption" + | "simnet"; gridSpan?: 1 | 2; }; diff --git a/apps/admin/src/sections/servers/form-schema/useProtocolFields.ts b/apps/admin/src/sections/servers/form-schema/useProtocolFields.ts index cab2a7a..f128b31 100644 --- a/apps/admin/src/sections/servers/form-schema/useProtocolFields.ts +++ b/apps/admin/src/sections/servers/form-schema/useProtocolFields.ts @@ -15,6 +15,10 @@ import { FLOWS, multiplexLevels, SECURITY, + SIMNET_AF_MAGIC_MODES, + SIMNET_AF_PATH_MODES, + SIMNET_CARRIERS, + SIMNET_FALLBACK_SCHEMES, SS_CIPHERS, TRANSPORTS, TUIC_CONGESTION, @@ -1221,6 +1225,499 @@ export function useProtocolFields() { condition: (p) => p.security === "reality", }, ], + simnet: [ + { + name: "ratio", + type: "number", + label: t("traffic_ratio", "Ratio"), + min: 0, + step: 0.01, + defaultValue: 1, + group: "basic", + }, + { + name: "port", + type: "number", + label: t("port", "Port"), + min: 1, + max: 65_535, + placeholder: "443", + group: "basic", + }, + { + name: "simnet_psk", + type: "input", + label: t("simnet_psk", "SimNet PSK"), + placeholder: t( + "simnet_psk_placeholder", + "Server pre-shared key (shared across all users)" + ), + generate: { + function: () => generatePassword(32), + }, + group: "basic", + }, + { + name: "simnet_ticket_id", + type: "input", + label: t("simnet_ticket_id", "SimNet Ticket ID"), + placeholder: t("simnet_ticket_id_placeholder", "Optional ticket id"), + group: "basic", + }, + { + name: "simnet_path", + type: "input", + label: t("simnet_path", "SimNet Path"), + placeholder: t("simnet_path_placeholder", "e.g. /simnet"), + group: "basic", + }, + { + name: "simnet_carrier", + type: "select", + label: t("simnet_carrier", "Carrier"), + options: SIMNET_CARRIERS, + defaultValue: "h2", + group: "basic", + }, + { + name: "up_mbps", + type: "number", + label: t("up_mbps", "Upload Bandwidth"), + min: 1, + placeholder: t( + "bandwidth_placeholder", + "Enter bandwidth, leave empty for BBR" + ), + suffix: "Mbps", + group: "basic", + }, + { + name: "down_mbps", + type: "number", + label: t("down_mbps", "Download Bandwidth"), + min: 1, + placeholder: t( + "bandwidth_placeholder", + "Enter bandwidth, leave empty for BBR" + ), + suffix: "Mbps", + group: "basic", + }, + { + name: "security", + type: "select", + label: t("security", "Security"), + options: ["none", "tls"], + defaultValue: "tls", + group: "security", + }, + { + name: "sni", + type: "input", + label: t("security_sni", "SNI"), + placeholder: t("sni_placeholder", "e.g. edge.example.com"), + group: "security", + }, + { + name: "allow_insecure", + type: "switch", + label: t("security_allow_insecure", "Allow Insecure"), + group: "security", + }, + { + name: "cert_mode", + type: "select", + label: t("cert_mode", "Certificate Mode"), + options: CERT_MODES, + defaultValue: "none", + group: "security", + }, + { + name: "cert_dns_provider", + type: "input", + label: t("cert_dns_provider", "DNS Provider"), + placeholder: "e.g. cloudflare, aliyun", + group: "security", + condition: (p) => p.cert_mode === "dns", + }, + { + name: "cert_dns_env", + type: "textarea", + label: t("cert_dns_env", "DNS Environment Variables"), + placeholder: + "CF_DNS_API_TOKEN=1234567890abcdefghijklmnopqrstuvwxyz\nALI_ACCESS_KEY_ID=your_access_key_id\nALI_ACCESS_KEY_SECRET=your_access_key_secret", + group: "security", + condition: (p) => p.cert_mode === "dns", + }, + { + name: "simnet_af_enabled", + type: "switch", + label: t("simnet_af_enabled", "Enable Anti-Fingerprint"), + group: "simnet", + }, + { + name: "simnet_af_path_mode", + type: "select", + label: t("simnet_af_path_mode", "AF Path Mode"), + options: SIMNET_AF_PATH_MODES, + defaultValue: "api", + group: "simnet", + condition: (p) => !!p.simnet_af_enabled, + }, + { + name: "simnet_af_path_prefix", + type: "input", + label: t("simnet_af_path_prefix", "AF Path Prefix"), + placeholder: t( + "simnet_af_path_prefix_placeholder", + "Optional path prefix" + ), + group: "simnet", + condition: (p) => !!p.simnet_af_enabled, + }, + { + name: "simnet_af_path_suffix", + type: "input", + label: t("simnet_af_path_suffix", "AF Path Suffix"), + placeholder: t( + "simnet_af_path_suffix_placeholder", + "Optional path suffix" + ), + group: "simnet", + condition: (p) => !!p.simnet_af_enabled, + }, + { + name: "simnet_af_magic_mode", + type: "select", + label: t("simnet_af_magic_mode", "AF Magic Mode"), + options: SIMNET_AF_MAGIC_MODES, + defaultValue: "derived", + group: "simnet", + condition: (p) => !!p.simnet_af_enabled, + }, + { + name: "simnet_af_response_jitter_ms", + type: "number", + label: t("simnet_af_response_jitter_ms", "AF Response Jitter"), + min: 0, + suffix: "ms", + defaultValue: 50, + group: "simnet", + condition: (p) => !!p.simnet_af_enabled, + }, + { + name: "simnet_af_handshake_polymorphism", + type: "switch", + label: t( + "simnet_af_handshake_polymorphism", + "Handshake Polymorphism" + ), + group: "simnet", + condition: (p) => !!p.simnet_af_enabled, + }, + { + name: "simnet_af_settings_jitter", + type: "switch", + label: t("simnet_af_settings_jitter", "Settings Jitter"), + group: "simnet", + condition: (p) => !!p.simnet_af_enabled, + }, + { + name: "simnet_af_fake_header_injection", + type: "switch", + label: t("simnet_af_fake_header_injection", "Fake Header Injection"), + group: "simnet", + condition: (p) => !!p.simnet_af_enabled, + }, + { + name: "simnet_reverse_enabled", + type: "switch", + label: t("simnet_reverse_enabled", "Enable Local Reverse Proxy"), + group: "simnet", + }, + { + name: "simnet_reverse_listen_addr", + type: "input", + label: t("simnet_reverse_listen_addr", "Reverse Listen Address"), + placeholder: "127.0.0.1", + group: "simnet", + condition: (p) => !!p.simnet_reverse_enabled, + }, + { + name: "simnet_reverse_listen_port", + type: "number", + label: t("simnet_reverse_listen_port", "Reverse Listen Port"), + min: 1, + max: 65_535, + placeholder: "18080", + group: "simnet", + condition: (p) => !!p.simnet_reverse_enabled, + }, + { + name: "simnet_reverse_target_host", + type: "input", + label: t("simnet_reverse_target_host", "Reverse Target Host"), + placeholder: "127.0.0.1", + group: "simnet", + condition: (p) => !!p.simnet_reverse_enabled, + }, + { + name: "simnet_reverse_target_port", + type: "number", + label: t("simnet_reverse_target_port", "Reverse Target Port"), + min: 1, + max: 65_535, + placeholder: "8080", + group: "simnet", + condition: (p) => !!p.simnet_reverse_enabled, + }, + { + name: "simnet_fallback_enabled", + type: "switch", + label: t( + "simnet_fallback_enabled", + "Enable HTTPS Fallback Reverse Proxy" + ), + group: "simnet", + }, + { + name: "simnet_fallback_target_scheme", + type: "select", + label: t("simnet_fallback_target_scheme", "Fallback Target Scheme"), + options: SIMNET_FALLBACK_SCHEMES, + defaultValue: "https", + group: "simnet", + condition: (p) => !!p.simnet_fallback_enabled, + }, + { + name: "simnet_fallback_target_host", + type: "input", + label: t("simnet_fallback_target_host", "Fallback Target Host"), + placeholder: t( + "simnet_fallback_target_host_placeholder", + "e.g. www.example.com" + ), + group: "simnet", + condition: (p) => !!p.simnet_fallback_enabled, + }, + { + name: "simnet_fallback_target_port", + type: "number", + label: t("simnet_fallback_target_port", "Fallback Target Port"), + min: 1, + max: 65_535, + placeholder: "443 / 80", + group: "simnet", + condition: (p) => !!p.simnet_fallback_enabled, + }, + { + name: "simnet_fallback_host_header", + type: "input", + label: t("simnet_fallback_host_header", "Fallback Host Header"), + placeholder: t( + "simnet_fallback_host_header_placeholder", + "Optional, defaults to target host" + ), + group: "simnet", + condition: (p) => !!p.simnet_fallback_enabled, + }, + { + name: "simnet_fallback_tls_sni", + type: "input", + label: t("simnet_fallback_tls_sni", "Fallback TLS SNI"), + placeholder: t( + "simnet_fallback_tls_sni_placeholder", + "Optional, defaults to target host" + ), + group: "simnet", + condition: (p) => !!p.simnet_fallback_enabled, + }, + { + name: "simnet_inbound_max_streams_per_session", + type: "number", + label: t( + "simnet_inbound_max_streams_per_session", + "Inbound Streams / Session" + ), + min: 1, + defaultValue: 128, + group: "simnet", + }, + { + name: "simnet_inbound_max_udp_streams_per_session", + type: "number", + label: t( + "simnet_inbound_max_udp_streams_per_session", + "Inbound UDP Streams / Session" + ), + min: 1, + defaultValue: 64, + group: "simnet", + }, + { + name: "simnet_inbound_max_handler_tasks_per_session", + type: "number", + label: t( + "simnet_inbound_max_handler_tasks_per_session", + "Handler Tasks / Session" + ), + min: 1, + defaultValue: 128, + group: "simnet", + }, + { + name: "simnet_stream_event_channel_capacity", + type: "number", + label: t( + "simnet_stream_event_channel_capacity", + "Stream Event Channel" + ), + min: 1, + defaultValue: 256, + group: "simnet", + }, + { + name: "simnet_stream_data_channel_capacity", + type: "number", + label: t( + "simnet_stream_data_channel_capacity", + "Stream Data Channel" + ), + min: 1, + defaultValue: 128, + group: "simnet", + }, + { + name: "simnet_target_dial_timeout_ms", + type: "number", + label: t("simnet_target_dial_timeout_ms", "Target Dial Timeout"), + min: 1, + suffix: "ms", + defaultValue: 12_000, + group: "simnet", + }, + { + name: "simnet_target_max_concurrent_dials", + type: "number", + label: t( + "simnet_target_max_concurrent_dials", + "Concurrent Target Dials" + ), + min: 1, + defaultValue: 256, + group: "simnet", + }, + { + name: "simnet_send_window", + type: "number", + label: t("simnet_send_window", "Send Window"), + min: 1, + suffix: "bytes", + defaultValue: 4_194_304, + group: "simnet", + }, + { + name: "simnet_recv_window", + type: "number", + label: t("simnet_recv_window", "Receive Window"), + min: 1, + suffix: "bytes", + defaultValue: 4_194_304, + group: "simnet", + }, + { + name: "simnet_max_concurrent_streams", + type: "number", + label: t("simnet_max_concurrent_streams", "H2 Concurrent Streams"), + min: 1, + defaultValue: 100, + group: "simnet", + }, + { + name: "simnet_initial_window_size", + type: "number", + label: t("simnet_initial_window_size", "H2 Initial Window"), + min: 1, + suffix: "bytes", + defaultValue: 65_535, + group: "simnet", + }, + { + name: "simnet_max_frame_size", + type: "number", + label: t("simnet_max_frame_size", "H2 Max Frame Size"), + min: 1, + suffix: "bytes", + defaultValue: 16_384, + group: "simnet", + }, + { + name: "simnet_client_max_concurrent_streams", + type: "number", + label: t( + "simnet_client_max_concurrent_streams", + "Client Concurrent Streams" + ), + min: 1, + defaultValue: 32, + group: "simnet", + }, + { + name: "simnet_client_max_streams_per_session", + type: "number", + label: t( + "simnet_client_max_streams_per_session", + "Client Streams / Session" + ), + min: 1, + defaultValue: 512, + group: "simnet", + }, + { + name: "simnet_client_session_idle_timeout_secs", + type: "number", + label: t( + "simnet_client_session_idle_timeout_secs", + "Client Session Idle Timeout" + ), + min: 1, + suffix: "s", + defaultValue: 90, + group: "simnet", + }, + { + name: "simnet_client_max_udp_sessions", + type: "number", + label: t("simnet_client_max_udp_sessions", "Client UDP Sessions"), + min: 1, + defaultValue: 64, + group: "simnet", + }, + { + name: "simnet_egress_block_loopback", + type: "switch", + label: t("simnet_egress_block_loopback", "Block Loopback Egress"), + group: "simnet", + }, + { + name: "simnet_egress_block_private", + type: "switch", + label: t("simnet_egress_block_private", "Block Private Egress"), + group: "simnet", + }, + { + name: "simnet_egress_block_link_local", + type: "switch", + label: t("simnet_egress_block_link_local", "Block Link-Local Egress"), + group: "simnet", + }, + { + name: "simnet_egress_block_metadata", + type: "switch", + label: t("simnet_egress_block_metadata", "Block Metadata Egress"), + group: "simnet", + }, + ], }), [t] ); diff --git a/apps/admin/src/sections/servers/server-form.tsx b/apps/admin/src/sections/servers/server-form.tsx index 5cfc0ec..60d6d45 100644 --- a/apps/admin/src/sections/servers/server-form.tsx +++ b/apps/admin/src/sections/servers/server-form.tsx @@ -696,6 +696,15 @@ export default function ServerForm(props: { i, current )} + {renderGroupCard( + t("simnet", "SimNet"), + fields, + "simnet", + control, + form, + i, + current + )}