zapret-kvn/docs/sing-box/template-format-v1.md
2026-03-27 14:15:12 +03:00

5.4 KiB

sing-box Template Format V1

Purpose

This document defines the first template format for editing sing-box configs inside Zapret KVN through a plain text editor.

This format is intentionally simple:

  • the user edits text;
  • the template stays valid JSON;
  • the app injects only a few runtime-managed fragments;
  • the final compiled JSON is what gets launched.

Baseline

Chosen Ownership Model

User-owned in template

The template should own:

  • log choices if the user wants to edit them;
  • user-defined outbounds other than the active runtime proxy outbound;
  • route.final;
  • user route rules;
  • DNS servers and DNS final selection;
  • optional extra experimental sections except app-managed entries.

App-owned at compile time

The app injects:

  • TUN inbound fragments;
  • hybrid protect inbound when needed;
  • active-node proxy outbound;
  • required runtime route pre-rules;
  • required Clash API object.

This keeps the editor text-first without losing the ability to switch active nodes and runtime mode safely.

Supported Placeholders

V1 supports these placeholders for sing-box.

${APP_INBOUNDS}

Context:

  • array splice placeholder

Placement:

"inbounds": [
  "${APP_INBOUNDS}"
]

Expansion:

  • native mode: one tun inbound
  • hybrid mode: one tun inbound plus one local shadowsocks protect inbound

${APP_PROXY_OUTBOUND}

Context:

  • array item placeholder

Placement:

"outbounds": [
  "${APP_PROXY_OUTBOUND}",
  {
    "type": "direct",
    "tag": "direct"
  }
]

Expansion:

  • native mode: converted active-node outbound tagged proxy
  • hybrid mode: local SOCKS relay outbound tagged proxy

${APP_ROUTE_RULES}

Context:

  • array splice placeholder

Placement:

"rules": [
  "${APP_ROUTE_RULES}",
  {
    "domain_suffix": ["discord.com"],
    "outbound": "proxy"
  }
]

Expansion:

  • sniff
  • hijack-dns for DNS protocol
  • protected-process bypass rule
  • native mode:
    • active server endpoint bypass rule
  • hybrid mode:
    • protect inbound direct rule

Notes:

  • this placeholder is intended for app-managed pre-rules;
  • user rules come after it in the template array.

${APP_CLASH_API}

Context:

  • value placeholder

Placement:

"experimental": {
  "clash_api": "${APP_CLASH_API}"
}

Expansion:

  • one object containing app-managed Clash API settings, currently at least external_controller

Required Placeholders

For V1, these placeholders are required:

  • ${APP_INBOUNDS}
  • ${APP_PROXY_OUTBOUND}
  • ${APP_ROUTE_RULES}
  • ${APP_CLASH_API}

Compile must fail if any of them is missing.

Required Runtime Tags

After compile, the template must contain these tags and references:

  • outbound tag proxy
  • outbound tag direct
  • outbound tag block
  • DNS server tag bootstrap-dns
  • DNS server tag proxy-dns

The template may define direct, block, bootstrap-dns, and proxy-dns itself. The app only verifies that they exist and that references are valid.

{
  "log": {
    "level": "warn",
    "timestamp": true
  },
  "inbounds": [
    "${APP_INBOUNDS}"
  ],
  "outbounds": [
    "${APP_PROXY_OUTBOUND}",
    {
      "type": "direct",
      "tag": "direct",
      "domain_resolver": "bootstrap-dns"
    },
    {
      "type": "block",
      "tag": "block"
    }
  ],
  "route": {
    "auto_detect_interface": true,
    "default_domain_resolver": "proxy-dns",
    "final": "proxy",
    "rules": [
      "${APP_ROUTE_RULES}",
      {
        "domain_suffix": ["discord.com"],
        "outbound": "proxy"
      }
    ]
  },
  "dns": {
    "servers": [
      {
        "tag": "bootstrap-dns",
        "type": "udp",
        "server": "1.1.1.1"
      },
      {
        "tag": "proxy-dns",
        "type": "tcp",
        "server": "8.8.8.8",
        "detour": "proxy"
      }
    ],
    "final": "proxy-dns"
  },
  "experimental": {
    "clash_api": "${APP_CLASH_API}"
  }
}

Validation Rules

Template-level

  • template parses as valid JSON;
  • placeholders appear only as full string values;
  • placeholders appear only in supported structural contexts.

Compile-level

  • active node exists;
  • active node can be rendered in native or hybrid mode;
  • every required placeholder exists exactly once;
  • no unknown placeholder exists.

Final-config-level

  • final JSON contains one or more inbounds from ${APP_INBOUNDS};
  • final JSON contains proxy, direct, and block outbounds;
  • route.final references a real outbound;
  • default_domain_resolver and dns.final point to real DNS server tags;
  • final JSON is accepted by the sing-box validator.

What This Format Avoids

This format intentionally avoids:

  • GUI field editors for route rules;
  • GUI field editors for DNS;
  • GUI field editors for transport and TLS;
  • ad hoc text macros embedded inside arbitrary strings;
  • direct editing of the exact launched config as the only source of truth.

Migration Note

This format is the preferred V1 path for sing-box.

If the same editor model works well, other engines can adopt the same pattern:

  • engine-native text
  • small placeholder set
  • app-managed compile stage
  • final compiled preview