diff --git a/frontend/src/test/__snapshots__/protocols.test.ts.snap b/frontend/src/test/__snapshots__/protocols.test.ts.snap
index 6682fe48..5ac334f2 100644
--- a/frontend/src/test/__snapshots__/protocols.test.ts.snap
+++ b/frontend/src/test/__snapshots__/protocols.test.ts.snap
@@ -1,5 +1,47 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+exports[`InboundSettingsSchema fixtures > parses http-basic byte-stably 1`] = `
+{
+ "protocol": "http",
+ "settings": {
+ "accounts": [
+ {
+ "pass": "proxypass",
+ "user": "proxyuser",
+ },
+ {
+ "pass": "guest123",
+ "user": "guest",
+ },
+ ],
+ "allowTransparent": false,
+ },
+}
+`;
+
+exports[`InboundSettingsSchema fixtures > parses hysteria-basic byte-stably 1`] = `
+{
+ "protocol": "hysteria",
+ "settings": {
+ "clients": [
+ {
+ "auth": "hyst3ria-v1-token-XYZ",
+ "comment": "legacy v1",
+ "email": "hy1-client@example.test",
+ "enable": true,
+ "expiryTime": 0,
+ "limitIp": 0,
+ "reset": 0,
+ "subId": "hy1-001",
+ "tgId": 0,
+ "totalGB": 0,
+ },
+ ],
+ "version": 1,
+ },
+}
+`;
+
exports[`InboundSettingsSchema fixtures > parses hysteria2-basic byte-stably 1`] = `
{
"protocol": "hysteria2",
@@ -23,6 +65,23 @@ exports[`InboundSettingsSchema fixtures > parses hysteria2-basic byte-stably 1`]
}
`;
+exports[`InboundSettingsSchema fixtures > parses mixed-basic byte-stably 1`] = `
+{
+ "protocol": "mixed",
+ "settings": {
+ "accounts": [
+ {
+ "pass": "sockspass",
+ "user": "socksuser",
+ },
+ ],
+ "auth": "password",
+ "ip": "127.0.0.1",
+ "udp": true,
+ },
+}
+`;
+
exports[`InboundSettingsSchema fixtures > parses shadowsocks-2022 byte-stably 1`] = `
{
"protocol": "shadowsocks",
@@ -73,6 +132,22 @@ exports[`InboundSettingsSchema fixtures > parses trojan-basic byte-stably 1`] =
}
`;
+exports[`InboundSettingsSchema fixtures > parses tunnel-basic byte-stably 1`] = `
+{
+ "protocol": "tunnel",
+ "settings": {
+ "allowedNetwork": "tcp,udp",
+ "followRedirect": false,
+ "portMap": {
+ "8080": "10.0.0.5:80",
+ "8443": "10.0.0.5:443",
+ },
+ "rewriteAddress": "1.1.1.1",
+ "rewritePort": 53,
+ },
+}
+`;
+
exports[`InboundSettingsSchema fixtures > parses vless-tcp-none byte-stably 1`] = `
{
"protocol": "vless",
diff --git a/frontend/src/test/__snapshots__/security.test.ts.snap b/frontend/src/test/__snapshots__/security.test.ts.snap
new file mode 100644
index 00000000..bbca671f
--- /dev/null
+++ b/frontend/src/test/__snapshots__/security.test.ts.snap
@@ -0,0 +1,72 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`SecuritySettingsSchema fixtures > parses none byte-stably 1`] = `
+{
+ "security": "none",
+}
+`;
+
+exports[`SecuritySettingsSchema fixtures > parses reality-basic byte-stably 1`] = `
+{
+ "realitySettings": {
+ "maxClientVer": "",
+ "maxTimediff": 0,
+ "minClientVer": "",
+ "mldsa65Seed": "",
+ "privateKey": "wM-2_oQRWXyLcXhV5q1ifTBcS3K8mYR3wQI3PqGFK1k",
+ "serverNames": [
+ "yahoo.com",
+ "www.yahoo.com",
+ ],
+ "settings": {
+ "fingerprint": "chrome",
+ "mldsa65Verify": "",
+ "publicKey": "Tx5yj1bRcOPHkdvT2pIAQ2zh0gQ8m4OPdnzqXJxxV3o",
+ "serverName": "",
+ "spiderX": "/",
+ },
+ "shortIds": [
+ "a3f1",
+ "b8c2",
+ "d9e4",
+ ],
+ "show": false,
+ "target": "yahoo.com:443",
+ "xver": 0,
+ },
+ "security": "reality",
+}
+`;
+
+exports[`SecuritySettingsSchema fixtures > parses tls-cert-file byte-stably 1`] = `
+{
+ "security": "tls",
+ "tlsSettings": {
+ "alpn": [
+ "h2",
+ "http/1.1",
+ ],
+ "certificates": [
+ {
+ "buildChain": false,
+ "certificateFile": "/etc/ssl/certs/cdn.example.test.crt",
+ "keyFile": "/etc/ssl/private/cdn.example.test.key",
+ "oneTimeLoading": false,
+ "usage": "encipherment",
+ },
+ ],
+ "cipherSuites": "",
+ "disableSystemRoot": false,
+ "echServerKeys": "",
+ "enableSessionResumption": false,
+ "maxVersion": "1.3",
+ "minVersion": "1.2",
+ "rejectUnknownSni": false,
+ "serverName": "cdn.example.test",
+ "settings": {
+ "echConfigList": "",
+ "fingerprint": "chrome",
+ },
+ },
+}
+`;
diff --git a/frontend/src/test/__snapshots__/stream.test.ts.snap b/frontend/src/test/__snapshots__/stream.test.ts.snap
new file mode 100644
index 00000000..616152af
--- /dev/null
+++ b/frontend/src/test/__snapshots__/stream.test.ts.snap
@@ -0,0 +1,34 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`NetworkSettingsSchema fixtures > parses grpc-basic byte-stably 1`] = `
+{
+ "grpcSettings": {
+ "authority": "grpc.example.test",
+ "multiMode": false,
+ "serviceName": "GunService",
+ },
+ "network": "grpc",
+}
+`;
+
+exports[`NetworkSettingsSchema fixtures > parses tcp-none byte-stably 1`] = `
+{
+ "network": "tcp",
+ "tcpSettings": {},
+}
+`;
+
+exports[`NetworkSettingsSchema fixtures > parses ws-default byte-stably 1`] = `
+{
+ "network": "ws",
+ "wsSettings": {
+ "acceptProxyProtocol": false,
+ "headers": {
+ "X-Forwarded-Proto": "https",
+ },
+ "heartbeatPeriod": 30,
+ "host": "cdn.example.test",
+ "path": "/api/v2",
+ },
+}
+`;
diff --git a/frontend/src/test/golden/fixtures/inbound/http-basic.json b/frontend/src/test/golden/fixtures/inbound/http-basic.json
new file mode 100644
index 00000000..c07c3fd9
--- /dev/null
+++ b/frontend/src/test/golden/fixtures/inbound/http-basic.json
@@ -0,0 +1,10 @@
+{
+ "protocol": "http",
+ "settings": {
+ "accounts": [
+ { "user": "proxyuser", "pass": "proxypass" },
+ { "user": "guest", "pass": "guest123" }
+ ],
+ "allowTransparent": false
+ }
+}
diff --git a/frontend/src/test/golden/fixtures/inbound/hysteria-basic.json b/frontend/src/test/golden/fixtures/inbound/hysteria-basic.json
new file mode 100644
index 00000000..77793d88
--- /dev/null
+++ b/frontend/src/test/golden/fixtures/inbound/hysteria-basic.json
@@ -0,0 +1,20 @@
+{
+ "protocol": "hysteria",
+ "settings": {
+ "version": 1,
+ "clients": [
+ {
+ "auth": "hyst3ria-v1-token-XYZ",
+ "email": "hy1-client@example.test",
+ "limitIp": 0,
+ "totalGB": 0,
+ "expiryTime": 0,
+ "enable": true,
+ "tgId": 0,
+ "subId": "hy1-001",
+ "comment": "legacy v1",
+ "reset": 0
+ }
+ ]
+ }
+}
diff --git a/frontend/src/test/golden/fixtures/inbound/mixed-basic.json b/frontend/src/test/golden/fixtures/inbound/mixed-basic.json
new file mode 100644
index 00000000..6754fe32
--- /dev/null
+++ b/frontend/src/test/golden/fixtures/inbound/mixed-basic.json
@@ -0,0 +1,11 @@
+{
+ "protocol": "mixed",
+ "settings": {
+ "auth": "password",
+ "accounts": [
+ { "user": "socksuser", "pass": "sockspass" }
+ ],
+ "udp": true,
+ "ip": "127.0.0.1"
+ }
+}
diff --git a/frontend/src/test/golden/fixtures/inbound/tunnel-basic.json b/frontend/src/test/golden/fixtures/inbound/tunnel-basic.json
new file mode 100644
index 00000000..901afd32
--- /dev/null
+++ b/frontend/src/test/golden/fixtures/inbound/tunnel-basic.json
@@ -0,0 +1,13 @@
+{
+ "protocol": "tunnel",
+ "settings": {
+ "rewriteAddress": "1.1.1.1",
+ "rewritePort": 53,
+ "portMap": {
+ "8080": "10.0.0.5:80",
+ "8443": "10.0.0.5:443"
+ },
+ "allowedNetwork": "tcp,udp",
+ "followRedirect": false
+ }
+}
diff --git a/frontend/src/test/golden/fixtures/security/none.json b/frontend/src/test/golden/fixtures/security/none.json
new file mode 100644
index 00000000..2cb07736
--- /dev/null
+++ b/frontend/src/test/golden/fixtures/security/none.json
@@ -0,0 +1,3 @@
+{
+ "security": "none"
+}
diff --git a/frontend/src/test/golden/fixtures/security/reality-basic.json b/frontend/src/test/golden/fixtures/security/reality-basic.json
new file mode 100644
index 00000000..f9434e2f
--- /dev/null
+++ b/frontend/src/test/golden/fixtures/security/reality-basic.json
@@ -0,0 +1,22 @@
+{
+ "security": "reality",
+ "realitySettings": {
+ "show": false,
+ "xver": 0,
+ "target": "yahoo.com:443",
+ "serverNames": ["yahoo.com", "www.yahoo.com"],
+ "privateKey": "wM-2_oQRWXyLcXhV5q1ifTBcS3K8mYR3wQI3PqGFK1k",
+ "minClientVer": "",
+ "maxClientVer": "",
+ "maxTimediff": 0,
+ "shortIds": ["a3f1", "b8c2", "d9e4"],
+ "mldsa65Seed": "",
+ "settings": {
+ "publicKey": "Tx5yj1bRcOPHkdvT2pIAQ2zh0gQ8m4OPdnzqXJxxV3o",
+ "fingerprint": "chrome",
+ "serverName": "",
+ "spiderX": "/",
+ "mldsa65Verify": ""
+ }
+ }
+}
diff --git a/frontend/src/test/golden/fixtures/security/tls-cert-file.json b/frontend/src/test/golden/fixtures/security/tls-cert-file.json
new file mode 100644
index 00000000..1590f800
--- /dev/null
+++ b/frontend/src/test/golden/fixtures/security/tls-cert-file.json
@@ -0,0 +1,27 @@
+{
+ "security": "tls",
+ "tlsSettings": {
+ "serverName": "cdn.example.test",
+ "minVersion": "1.2",
+ "maxVersion": "1.3",
+ "cipherSuites": "",
+ "rejectUnknownSni": false,
+ "disableSystemRoot": false,
+ "enableSessionResumption": false,
+ "certificates": [
+ {
+ "certificateFile": "/etc/ssl/certs/cdn.example.test.crt",
+ "keyFile": "/etc/ssl/private/cdn.example.test.key",
+ "oneTimeLoading": false,
+ "usage": "encipherment",
+ "buildChain": false
+ }
+ ],
+ "alpn": ["h2", "http/1.1"],
+ "echServerKeys": "",
+ "settings": {
+ "fingerprint": "chrome",
+ "echConfigList": ""
+ }
+ }
+}
diff --git a/frontend/src/test/golden/fixtures/stream/grpc-basic.json b/frontend/src/test/golden/fixtures/stream/grpc-basic.json
new file mode 100644
index 00000000..e1fb8824
--- /dev/null
+++ b/frontend/src/test/golden/fixtures/stream/grpc-basic.json
@@ -0,0 +1,8 @@
+{
+ "network": "grpc",
+ "grpcSettings": {
+ "serviceName": "GunService",
+ "authority": "grpc.example.test",
+ "multiMode": false
+ }
+}
diff --git a/frontend/src/test/golden/fixtures/stream/tcp-none.json b/frontend/src/test/golden/fixtures/stream/tcp-none.json
new file mode 100644
index 00000000..d0e3b193
--- /dev/null
+++ b/frontend/src/test/golden/fixtures/stream/tcp-none.json
@@ -0,0 +1,4 @@
+{
+ "network": "tcp",
+ "tcpSettings": {}
+}
diff --git a/frontend/src/test/golden/fixtures/stream/ws-default.json b/frontend/src/test/golden/fixtures/stream/ws-default.json
new file mode 100644
index 00000000..64cc3bdd
--- /dev/null
+++ b/frontend/src/test/golden/fixtures/stream/ws-default.json
@@ -0,0 +1,12 @@
+{
+ "network": "ws",
+ "wsSettings": {
+ "acceptProxyProtocol": false,
+ "path": "/api/v2",
+ "host": "cdn.example.test",
+ "headers": {
+ "X-Forwarded-Proto": "https"
+ },
+ "heartbeatPeriod": 30
+ }
+}
diff --git a/frontend/src/test/security.test.ts b/frontend/src/test/security.test.ts
new file mode 100644
index 00000000..f9407315
--- /dev/null
+++ b/frontend/src/test/security.test.ts
@@ -0,0 +1,26 @@
+///
+import { describe, expect, it } from 'vitest';
+
+import { SecuritySettingsSchema } from '@/schemas/protocols';
+
+const securityFixtures = import.meta.glob(
+ './golden/fixtures/security/*.json',
+ { eager: true, import: 'default' },
+);
+
+function fixtureName(path: string): string {
+ const file = path.split('/').pop() ?? path;
+ return file.replace(/\.json$/, '');
+}
+
+describe('SecuritySettingsSchema fixtures', () => {
+ const entries = Object.entries(securityFixtures).sort(([a], [b]) => a.localeCompare(b));
+ expect(entries.length, 'expected at least one fixture under golden/fixtures/security').toBeGreaterThan(0);
+
+ for (const [path, raw] of entries) {
+ it(`parses ${fixtureName(path)} byte-stably`, () => {
+ const parsed = SecuritySettingsSchema.parse(raw);
+ expect(parsed).toMatchSnapshot();
+ });
+ }
+});
diff --git a/frontend/src/test/stream.test.ts b/frontend/src/test/stream.test.ts
new file mode 100644
index 00000000..7621a18c
--- /dev/null
+++ b/frontend/src/test/stream.test.ts
@@ -0,0 +1,26 @@
+///
+import { describe, expect, it } from 'vitest';
+
+import { NetworkSettingsSchema } from '@/schemas/protocols';
+
+const streamFixtures = import.meta.glob(
+ './golden/fixtures/stream/*.json',
+ { eager: true, import: 'default' },
+);
+
+function fixtureName(path: string): string {
+ const file = path.split('/').pop() ?? path;
+ return file.replace(/\.json$/, '');
+}
+
+describe('NetworkSettingsSchema fixtures', () => {
+ const entries = Object.entries(streamFixtures).sort(([a], [b]) => a.localeCompare(b));
+ expect(entries.length, 'expected at least one fixture under golden/fixtures/stream').toBeGreaterThan(0);
+
+ for (const [path, raw] of entries) {
+ it(`parses ${fixtureName(path)} byte-stably`, () => {
+ const parsed = NetworkSettingsSchema.parse(raw);
+ expect(parsed).toMatchSnapshot();
+ });
+ }
+});