This repository is the organization Pages repository rust-proxy.github.io, whose Pages site root is https://rust-proxy.github.io/. Each project is an independent static site under its own subpath; the root provides a portal page.
- TUIC documentation: rust-proxy.github.io/tuic.
- Configuration editor (renders TUIC configuration by default): rust-proxy.github.io/config-editor/. It has its own HTML, CSS, WebAssembly, and theme, and runs standalone on any static server without depending on a documentation site or backend.
- Wind documentation: rust-proxy.github.io/wind, with English specifications at rust-proxy.github.io/wind/specs and a blog at rust-proxy.github.io/wind/blog.
The sites are built with Zensical; the configuration editor is built with Rust WASM + Svelte 5.
Requires Rust stable, the wasm32-unknown-unknown target, and Node.js 22.12+ (CI uses 24). Run all of the following commands from this repository's root:
rustup target add wasm32-unknown-unknown
npm ci --prefix config-editor
npm run dev --prefix config-editorOpen http://127.0.0.1:8080/. No Python or Zensical server is needed. Its application-schema selector currently provides independent TUIC server and client editors, covering multiple users, three certificate modes, SOCKS5 authentication, logging, connections, TCP/UDP forwarding, Quinn/quiche backends, outbound and ACL routing, DNS/GeoData, RESTful management, and HTTP/3 masquerading. The page uses a responsive workbench: a keyboard-accessible section directory with active-section and error indicators, a searchable card-based editor form, and a sticky preview region that renders the live generated config with syntax highlighting, per-line XML explanations on hover or keyboard focus, validation, and copy/download. Medium screens use a compact section selector; narrow screens switch between editing and preview/export while retaining input and scroll positions. Search matches visible field labels, hints, and paths without indexing input values. Error links switch back to editing before focusing the field. Invalid inputs keep the preview available: flagged values are shown as <placeholder> alongside the validation warnings, while copy and download stay disabled until every field is valid. A header selector switches between the embedded Catppuccin flavors (Latte, Frappé, Macchiato, Mocha), driven by a registry in ThemeStore and semantic design tokens.
When just is installed, the repository root provides shortcuts:
just setup # install locked dependencies for the first time
just dev # compile WASM and start the editor
just dev-ui # skip the WASM build when only changing Svelte/CSS
just wasm # recompile Rust/XML while Vite is running
just check # Rust, WASM, and Svelte checks
just browser # build the editor, then run the Chromium/Firefox E2E suite
just browser-site # build the assembled site, then test it at /config-editor/
just test-browser # Svelte component tests in real browsersRun just to see all build, preview, and browser-regression commands.
Standalone build:
npm run build --prefix config-editornpm run build compiles the Rust library with the locked wasm-pack, runs the Svelte/TypeScript checks and the frontend unit tests, and then bundles the local JS/CSS/WASM with Vite. The first build downloads the wasm-bindgen tool matching the Cargo lock file. npm run dev compiles WASM first and then starts Vite; Svelte/CSS supports hot reloading. After changing Rust or XML, run npm run wasm --prefix config-editor in another terminal and refresh the browser.
The default build embeds every application schema listed in config-editor/schema/schemas.txt; the browser switches between them without fetching XML. CONFIG_SCHEMA still selects one XML for isolated reuse tests; paths are relative to config-editor/ (or absolute). Alternative builds should output to a separate directory, and the environment and default WASM must be restored afterward; see the editor's DSL guide.
Artifacts live in config-editor/dist/. Hand the entire directory to a static server; it uses relative asset paths by default and supports either the root path or a subpath with a trailing /. The server must return application/wasm for .wasm; do not open the files over file://. For a fixed prefix, use npm run build --prefix config-editor -- --base /your-prefix/.
Credentials are generated with the browser Crypto API. All input, validation, and serialization happen locally in WASM; no third-party analytics scripts are loaded, inputs, themes, and credentials are not saved, and configuration is never submitted over the network. Copy and download include plaintext passwords, while the preview hides passwords by default.
The page accepts a schema query parameter for direct links; it is an application-schema ID from schema/schemas.txt (currently tuic-server or tuic-client). The selector keeps this parameter synchronized without discarding unrelated query parameters or the URL fragment. The legacy mode parameter is still read but no longer switches views. Unknown schemas safely fall back to the first registered schema.
Requires uv; uvx fetches the pinned Zensical and Python on demand, so no project virtual environment is created.
just docs
just docs-windEach documentation site has its own zensical.toml and docs/; the development server serves only one site at a time, and the configuration editor runs separately with the Vite server above. When adding a documentation site, create <project>/zensical.toml and <project>/docs/, and add it to the build recipe in justfile and to the portal links.
# Rust core logic, DSL, configuration, and security boundaries
cargo test --workspace --locked
cargo +nightly fmt --all --check
cargo clippy --workspace --all-targets --locked -- -D warnings
cargo clippy --target wasm32-unknown-unknown --lib --locked -- -D warnings
# Check Svelte/TypeScript and run frontend unit tests after building WASM (build also runs this)
npm run check --prefix config-editor
npm run test --prefix config-editor
# Svelte component tests in real browsers (Chromium + Firefox; needs `npm run wasm` first)
npm run test:browser --prefix config-editor
# Build all documentation sites and the standalone editor into site/; no publishing
just build
cargo +nightly -Zscript tests/config-editor/check-site.rs
# Assemble a site preview (/, /tuic/, /wind/)
node tests/config-editor/serve.mjs --dir site --port 8765Preview: http://127.0.0.1:8765/, http://127.0.0.1:8765/tuic/, http://127.0.0.1:8765/config-editor/, http://127.0.0.1:8765/wind/. Run npm ci --prefix config-editor first to install the frontend dependencies; the combined build clean-builds each documentation site, places the standalone editor under site/config-editor/, and copies portal/index.html to site/index.html. Stop the documentation development servers before a clean build to avoid cache conflicts.
# Generate test credentials and configuration dynamically; writes only to the ignored .cache/
cargo run --locked --example fixtures -- .cache/config-editor-fixtures
# Independent TOML/JSON/YAML parsers as a nightly cargo script
cargo +nightly -Zscript tests/config-editor/roundtrip.rs .cache/config-editor-fixtures/roundtrip.json
# Call the neighboring TUIC's real parsing functions and run a local SOCKS5 -> TUIC -> TCP echo
cargo +nightly -Zscript tests/config-editor/check-rust.rs --offlineThe real parsing check requires the neighboring ../tuic, its submodules, cached dependencies, and the corresponding build tools; omit --offline when the dependency cache is missing. The auxiliary Cargo project writes only to .cache/, starts from TUIC's lock file, and does not modify TUIC manifests, sources, lock files, or submodules. The loopback allowance applies only to the in-memory test configuration, and error diagnostics never print configuration contents.
End-to-end tests use Playwright Test with Chromium and Firefox. A separate npm manifest and lock file keep the runner out of the application bundle, and each config launches the dependency-free Node server tests/config-editor/serve.mjs through Playwright's webServer. Run from the repository root:
npm ci --prefix tests/config-editor
npm exec --prefix tests/config-editor --no-install -- playwright install chromium firefox
# Standalone build served at the root (default Playwright config)
npm test --prefix tests/config-editor
# Assembled Pages build served at /config-editor/ (run `just build` first)
npm run test:site --prefix tests/config-editor
# XML reuse with schema/example.xml built into .cache/generic-site
CONFIG_SCHEMA=schema/example.xml npm run build --prefix config-editor -- --outDir .cache/generic-site
npm run test:generic --prefix tests/config-editor
# Refresh visual baselines (Linux only)
npm run test:update --prefix tests/config-editorSpecs live in tests/config-editor/e2e/: editor.spec.ts (client and server schemas, TLS, routing, URL state, export, injection safety), generic.spec.ts (alternate XML description and Crypto-failure handling), and visual.spec.ts (Catppuccin flavors at desktop and mobile sizes). Chromium and Firefox projects run in parallel; traces, screenshots, and video are kept on failure and the HTML report is uploaded by CI. Visual baselines live under e2e/__screenshots__/linux/ and are compared only on Linux so fonts stay deterministic; other platforms skip them. Coverage still includes WASM loading, user removal, input validation, forwarding edits, copy/download, escaping, mobile, theming, no external requests, and no input persistence.
Config DSL v6 uses independent XML files to statically describe inputs, defaults, enums, conditions, lists, mappings, per-line output descriptions, and sensitive fields, deserialized with quick-xml + Serde; configuration descriptions are not written with Rust macros or closures. v6 adds root locales/default-locale attributes and a locale-partitioned <strings> pool whose entries are referenced with @string/<name> (missing locales fall back to the default, and builtin.* ids may be overridden). schema/schemas.txt registers the XML files embedded in the production selector, with one locale:label column per locale. The Rust session produces the form view, and Svelte renders it; generic projection and redaction live in dsl.rs, generic validation and field linkage in dsl/rules.rs, and basic address checks in validation.rs. TUIC branding, page sections, hints, cross-field rules, random-value generation declarations, and export commands are also entirely provided by XML. Frontend chrome text is compiled with Paraglide JS (inlang) under config-editor/{project.inlang,messages} into ui/paraglide/ (ignored). Adding a target application requires a new XML file and one manifest entry; schema/example.xml provides a bilingual reuse example with no TUIC fields.
Configuration state is modified only by the Rust Session. Svelte submits generic field/collection operations and reads field display values, visibility, errors, and previews from Snapshot; per-line preview explanations are read from the declared output descriptions in Rust (Snapshot.preview_lines), so the frontend only highlights and displays them. It does not parse XML, evaluate conditions, or keep a second mutable copy of the configuration. JSON strings cross the WASM boundary, and both field display values and stable row identities are strings, avoiding JavaScript number precision loss. ui/types.ts corresponds to the display contract in session/view.rs; when changing the contract, update both sides and run the session tests plus both browser test suites. Copy and download obtain the original text through a separate export operation rather than reading the redacted preview.
| Path | Contents |
|---|---|
Cargo.toml / Cargo.lock |
Editor Rust workspace and locked dependencies |
tuic/zensical.toml / tuic/docs/ |
TUIC Chinese documentation, navigation, field descriptions, and DSL documentation |
tuic/overrides/ |
TUIC theme overrides and 404 page |
config-editor/ |
Independently buildable Rust WASM + Svelte single-page application |
config-editor/ui/ |
Layered frontend: bridge/ (WASM/JSON boundary), state/ (session, workbench, theme registry, viewport, URL), lib/ (DOM and Prism helpers), components/ (BEM + Svelte scoped styles), styles/ (theme.css Catppuccin flavors and semantic tokens, base.css, primitives.css), and the types.ts display contract; unit tests use Vitest + jsdom |
config-editor/src/session.rs / session/view.rs |
Natively testable editing operations, form view, and preview export |
config-editor/src/wasm.rs |
WASM interface and browser Crypto API randomness adapter |
config-editor/package.json / vite.config.js |
Locked frontend tooling and static asset bundling |
config-editor/schema/schemas.txt |
Application-schema IDs, selector labels, and embedded XML sources |
config-editor/schema/tuic-server.xml / tuic-client.xml |
Independent TUIC server and client definitions |
config-editor/src/dsl/xml.rs / dsl/wire.rs / dsl/parser.rs |
XML subset checks, Serde data model, and semantic validation |
config-editor/src/dsl.rs |
Data projection, type checking, and redaction |
config-editor/src/schema.rs |
Embedded XML, cached parse results, generic state, and stable row identities |
config-editor/src/dsl/metadata.rs / dsl/rules.rs |
Page metadata, random-value declarations, validation, and field linkage |
config-editor/schema/example.xml |
Complete application reuse example with no TUIC fields |
config-editor/src/model.rs |
Configuration generation entry point and three-format serialization |
config-editor/tests/ |
XML DSL and configuration regression tests |
wind/zensical.toml / wind/docs/ |
Wind Chinese protocol specifications and design documents (single publishing source) |
wind/docs/specs/ |
Wind English specifications and RFC template, published under /wind/specs/ and kept in sync with the Chinese editions |
wind/docs/blog/ |
Wind blog: index.md entry point, posts/ articles, and .authors.yml; built with the native Zensical blog plugin and published under /wind/blog/ |
portal/index.html |
Site root portal page |
justfile |
Build, check, and preview recipes; the build recipe assembles all documentation sites and the editor into site/ without publishing |
tests/config-editor/ |
Nightly cargo-script checks (roundtrip.rs, check-rust.rs, check-site.rs), the dependency-free serve.mjs, and the Playwright Test specs and configs under e2e/ |
.github/workflows/deploy.yml |
GitHub Pages build and publish workflow |
The main documentation is maintained only in Simplified Chinese; the English specifications and RFC template under wind/docs/specs/ are the exception, published alongside their Chinese counterparts under /wind/specs/, with section numbering and requirements kept in sync. After updating TUIC or Wind, verify the editor's version baseline and the actual runtime behavior of fields, and do not expose configuration that is not yet wired into client runtime logic as usable functionality. Examples use placeholder domains and test credentials generated at runtime, and do not include real deployment data.
The Wind site enables Zensical's native blog plugin (available since Zensical 0.0.64) and the rss plugin (since 0.0.65); the pinned zensical version in the justfile must stay at or above 0.0.65. Blog articles live in wind/docs/blog/posts/, require a date in front matter, and use <!-- more --> as the excerpt separator; only the blog/index.md entry point belongs in nav, never individual posts. [project.plugins.rss] is restricted to match_path = "blog/posts/.*" so only posts (not generated archive, category, or author views) enter the feed, and it emits feed_rss_created.xml under /wind/. Keep blog content in Simplified Chinese like the rest of the site.
The CI and Pages workflow runs on pull requests, pushes to main, and manual triggers:
check: nightly rustfmt, stable native and WASM Clippy, Rust/XML DSL tests, and independent TOML/JSON/YAML parsing round trips through nightly cargo scripts. Zensical still runs on demand withuvx, with Python pinned to 3.13 viaUV_PYTHON.build: builds the standalone SPA with wasm-pack, the Svelte checker, and Vite, assembles all documentation sites, checks site links and assets with thecheck-site.rscargo script, and then runs the TUIC and no-TUIC-field XML reuse browser regressions through Playwright against Chromium and Firefox. Rust, uv, and npm use dependency caching.deploy: depends oncheckandbuildsucceeding, and publishes only on pushes tomainor manual runs; Pages write and OIDC permissions are granted only to this job, while pull requests only validate and build.
The published artifact is assembled in a temporary directory whose root is https://rust-proxy.github.io/: the portal page is at /, TUIC documentation at /tuic/, the standalone editor at /config-editor/, Wind Chinese documentation at /wind/, the Wind blog at /wind/blog/, the Wind RSS feed under /wind/, and the Wind English specifications at /wind/specs/. No custom domain or CNAME is used, and the Pages source should be set to GitHub Actions. Real TUIC parsing and loopback tests still run in an environment with the neighboring repositories as described above.
The documentation has migrated from MkDocs to Zensical and no longer uses the i18n plugin. The old /tuic/zh/ path does not generate a redirect; external links should be updated under /tuic/. A passing site build and configuration parse does not mean remote DNS, certificates, firewalls, or proxy connections have been verified.