Commit Graph

64 Commits

Author SHA1 Message Date
ospab 108bab6a90 fix(client): retry resume reconnects; add a way to hash the panel password
Two unrelated user-reported blockers.

Resume on desktop. The suspend/resume detector fired a single reconnect
attempt roughly half a second after waking — which is exactly when the NIC
has not reassociated yet, so it almost always failed. Failure then fell back
to the ordinary 25s stall heuristic, which keys off a monotonic clock that
does not advance while the machine is asleep, so it could take another 25s of
real uptime to fire, or never fire. The forced reconnect is now sticky:
retried every 3s until a session is actually established, verified via
last_valid_recv rather than by "an attempt was made".

Panel password. `api.password_hash` wants a hash, and nothing in the CLI
could produce one: `ostp init server` emits password_hash: "" and the only
generator was inline in the Server+Panel wizard branch, which is Unix-only —
so on a plain server there was no supported path to working API auth at all.
Adds `ostp hash-password [PASSWORD]`, prompting when the argument is omitted
so the password stays out of shell history. Output verified to match both
handle_login's comparison and a reference SHA-256.
2026-07-31 19:02:03 +03:00
ospab 0c69617725 fix(cli): trait-qualify Sha256::digest so it builds with or without the import
Follow-up to the v0.4.2-beta.3 CI break. Importing sha2::Digest fixed the
build there but the import reads as unused locally (different dependency
resolution), leaving a permanent warning in every build. Calling through
<sha2::Sha256 as sha2::Digest>::digest resolves the trait method
explicitly, so it compiles in both environments with no import and no
warning. Workspace now builds clean.
2026-07-30 14:41:16 +03:00
ospab 2092e22a7c fix(cli): missing sha2::Digest import broke the CI build (v0.4.2-beta.3)
Sha256::digest() is a trait method (from digest::Digest, re-exported as
sha2::Digest), not an inherent one - fully-qualifying the call
(sha2::Sha256::digest(...)) doesn't exempt it from Rust's requirement that
the trait itself be in scope for method resolution. Whatever made this
resolve locally without the explicit import didn't reproduce on the CI
runner's dependency resolution, breaking `cargo check` and killing the
whole v0.4.2-beta.3 matrix before a single platform job even started.
Added the import; harmless even where it isn't strictly needed.
2026-07-21 18:17:49 +03:00
ospab 9a891310f9 fix(cli): setup wizard used a fake password hash, locking admins out of their own panel
The Server+Panel setup wizard's panel-password hashing was a placeholder:
std::collections::hash_map::DefaultHasher (SipHash, not cryptographic, and
not even a 256-bit output - only the first 8 of 32 bytes were real, the
rest zero-padded), left in by the comment "sha2 is not a direct dep of
ostp/Cargo.toml, so we use std's hasher as a placeholder digest here."

api.rs's handle_login computes the REAL SHA256 hex digest of the submitted
password and compares it against config.json's stored password_hash. Since
the wizard's placeholder never produces the same value as real SHA256 of
the same password, anyone who set up a panel through this wizard could
never actually log into it with the password it just showed them - a
complete functional break of the wizard-driven admin flow, not a corner
case.

Added sha2 as a direct ostp dependency and replaced the placeholder with
the exact same format!("{:x}", Sha256::digest(..)) api.rs's login check
uses.
2026-07-18 18:14:19 +03:00
ospab 1c291d9c88 fix(release): the second branch is 'beta', not 'pre-release' — was never checkoutable
scripts/gha.ps1's -Branch ValidateSet accepted 'pre-release' and would
`git checkout pre-release` to promote alpha, but no such branch has ever
existed in this repo — only `beta` does (confirmed: `git branch -a`, and
the existing 0.4.6-beta/0.4.7-beta release history was cut from `beta`).
The very first beta release under the new gha.ps1 versioning scheme would
have failed outright on the checkout step.

This naming mismatch had spread through the whole release surface:
  - scripts/gha.ps1: -Branch ValidateSet + all internal checks
  - .github/workflows/release.yml: a dead branch-name check (harmless only
    because the workflow currently triggers on tag-push, not branch-push)
    plus two comments
  - CONTRIBUTING.md / .ru.md: branch-strategy table documented a
    `pre-release` branch that doesn't exist
  - README.md / .ru.md and ostp/src/main.rs: the `ostp update -b <name>`
    CLI help text/docs
  - scripts/install.sh: the channel match the CLI flag feeds into

Renamed all of it to `beta` to match the branch that actually exists.
Left scripts/gha.ps1:21's "semver pre-release identifier" alone — that's
the generic semver spec term, unrelated to the branch name, and got
reverted after a blanket replace briefly clobbered it.

Note: install.sh's alpha/beta self-update paths still assume a rolling
GitHub release tagged literally "alpha"/"beta" exists, which no gha.ps1
release ever publishes (only versioned tags like v0.4.7-beta.3) - that's
a separate, real bug, tracked apart from this rename since fixing it needs
either a floating tag from gha.ps1 or an API-query rewrite of install.sh.
2026-07-12 02:12:57 +03:00
ospab b7bd8c20a5 docs: update CLI arguments to subcommands 2026-07-10 03:05:30 +03:00
ospab 1d1a1ea5af refactor: remove dead stealth_sni config field across the whole stack
stealth_sni was never actually consumed to construct any wire bytes —
verified dead in bridge.rs (only stored, never read). It implied
TLS/HTTP SNI mimicry that this project deliberately does not do
(zapret-like: packet-level DPI obfuscation only, no protocol
mimicry). Removed from the runtime schema (config.rs, bridge.rs),
both CLI/GUI local config shapes and their JSON templates, the
Flutter profile model/UI/share-link logic, and README feature docs.
migrate.rs now drops the field from legacy configs with a note
instead of carrying it forward.
2026-07-10 01:04:56 +03:00
ospab 5dc3a60017 refactor(logging): consolidate all logs into one ostp.log, Windows clears on start
Every process (CLI daemon, GUI, TUN helper) and every subsystem (tracing, the
core event logger, the helper IPC, panic hook) wrote its own file: ostp-cli.log
+ ostp-core.log + ostp-helper.log + ostp-crash.log — a pile per run. Now they
all funnel into a single ostp.log next to the exe.

- logging: LOG_FILE_NAME/log_file_path() as the one source of truth; init_tracing
  gains a `truncate` arg. Truncation is gated twice: Windows-only (cfg!(windows))
  AND daemon-only. One-shot commands (gk/check/init/-V/...) and the elevated TUN
  helper pass truncate=false so they can never wipe a running daemon's log;
  invocation_is_daemon() detects the daemon from argv. On Linux the server always
  appends (history kept, OS-rotated) as requested.
- runner/helper manual writers + panic hook now target log_file_path(), so their
  output lands in the same ostp.log instead of separate files.
2026-07-09 14:32:08 +03:00
ospab 1568db3323 ci: rename nightly to alpha 2026-07-09 02:46:47 +03:00
ospab d822f48891 refactor(config): one canonical config.json schema for client/server/relay
All three on-disk config.json shapes (client, server, relay) used to be
declared locally inside ostp/src/main.rs, invisible to any other consumer —
which is exactly how ostp_client::migrate ended up matching against loosely
typed serde_json::Value instead of a real schema, with no guarantee its
hand-built output actually matched what the CLI parser expected.

Moved every one of those definitions (AppMode, UnifiedConfig, ServerConfig,
RelayServerConfig, ClientFileConfig, TunConfig, ExcludeConfig, MuxConfig,
TransportConfigRaw, ApiConfig, FallbackCfg, ListenConfig, UserConfig) into
ostp_client::config — the same file that already held the runtime
ClientConfig/OstpConfig/etc. main.rs now imports them instead of
re-declaring them (`ClientFileConfig as ClientConfig` to avoid colliding
with the runtime ClientConfig, which stays separate on purpose: it's the
engine's internal shape — handshake/io timeouts and the like a user never
sets in config.json — built FROM one of these via the mapping in
run_client_directly, not the same thing).

ServerConfig.dns is now Option<serde_json::Value> rather than
Option<ostp_server::dns::DnsConfig> — ostp-client doesn't (and shouldn't)
depend on ostp-server just to name that type. main.rs, which already depends
on both crates, deserializes it right before handing it to run_server().

cmd_migrate now proves its output against this schema before ever writing
to disk (serde_json::from_value::<UnifiedConfig>(migrated)) — a migrator/
schema drift is now a hard error at migrate time, not a corrupted
config.json discovered later. Added a matching unit test
(every_migrated_output_matches_the_canonical_schema) that exercises this
same check on all three migration paths (modular, legacy-flat, server).
2026-07-08 18:58:34 +03:00
ospab 26665a826f feat(client): one authoritative config migrator, manual-only
- fix(cli): stop printing the startup banner ("ostp-cli vX.Y.Z | OS: ...")
  to stderr on every single command invocation. init_tracing() ran
  unconditionally before command dispatch, so `ostp -V`, `ostp gk`, etc. all
  showed it. It's still written to the log file (useful there), just no
  longer echoed via the stderr tracing layer for one-shot commands.

- feat(client): add ostp-client::migrate, the ONE place config migration
  runs. Previously there were three uncoordinated migration paths: a Python
  snippet embedded in scripts/install.sh (only touched server api.* fields,
  ran on every update), the old 0.3.x line's auto-migration on every hot
  reload (silent besides a log warning), and nothing at all for the current
  rebuild. Consolidated into one module covering every config shape that's
  actually existed:
    - v0.3.1-v0.3.21 modular (inbounds/outbounds/routing) -> current flat
      schema, including correctly resolving routing.default_outbound through
      a urltest/selector group to the real server, and reporting (not
      silently dropping) every additional server a multi-server config had.
    - pre-0.3.1 flat configs carrying now-dead fields (tun.wintun_path,
      tun.ipv4_address, transport.wss) -> dropped with an explicit reason,
      everything else passes through untouched.
    - server configs -> backfills api.* defaults and drops legacy api.token
      (ported straight from the install.sh Python, same behavior, correct
      place).
  6 unit tests cover all of the above against realistic fixtures. Wired up
  as `ostp migrate` (was missing from Commands entirely) — no other code
  path calls into this module, so a config's shape only ever changes when
  explicitly asked.

- feat(cli): `ostp import <url>` now asks the same TUN/mux/debug questions
  `ostp connect <url>` always did. Previously import just wrote flat
  defaults to disk with no way to turn any of that on short of hand-editing
  the resulting config.json afterward. Extracted the shared prompt into
  prompt_client_options() so both paths stay in sync.

- chore(install): remove the embedded Python config-migration snippet from
  install.sh; schema migration must never happen implicitly during an
  install/update. Points users at `ostp migrate` instead.
2026-07-08 18:45:04 +03:00
ospab c330a0abe3 Fix UAC diagnostics parity, gk alias, flutter version, and versioned CI channels
- GUI launch_as_admin now matches the CLI's UAC diagnosis: detects
  ERROR_CANCELLED (1223, user declined the prompt) instead of silently
  treating it as success, and reports GetLastError()+exe path for any other
  ShellExecuteW failure, replacing the old single opaque "denied or missing"
  message that made GUI/TUI failures impossible to tell apart.
- generate-key subcommand renamed to `gk` (kept `generate-key` as an alias).
- Fixed a real short-flag collision: GenerateKey's --count used short='c',
  which collides with the global --config short (propagated into every
  subcommand); clap validates the whole command tree on first parse(), so
  this could break parsing for the entire CLI, not just generate-key/gk.
  --count is now short='n'.
- ostp-flutter/pubspec.yaml version was stuck at 0.2.97+12; bumped to 0.4.1+13.
- release.yml: added a resolve-channel job that computes one release tag per
  run instead of repeating the logic in five upload steps. Rolling channel
  pushes now carry the actual Cargo.toml version instead of a bare channel
  name: `{version}-nightly` for the nightly branch, `{version}-beta` for
  pre-release. workflow_dispatch gained a `channel` input restricted to
  nightly/beta only — a manual run can never accidentally publish a "stable"
  release; that still requires an explicit vX.Y.Z tag push.
2026-07-08 02:52:23 +03:00
ospab b2ee9eb010 Port CLI subcommands + multi-channel release onto the 0.4.0 rebuild
Brings the useful work from the old master/pre-release lineage (5c2b5a0)
onto the clean rebuild, since that lineage never had the multi-server/WSS/
Reality removal or any of the 0.4.0 stability work. This is a manual port,
not a cherry-pick — this file's Args/ClientConfig shape had already
diverged too much for the patch to apply mechanically.

- ostp/src/main.rs: flat Args -> clap subcommands (setup, init, generate-key,
  links, check, connect, uninstall, update, import, proxy-env,
  proxy-env-clear), bridged onto the existing ~500 lines of flag-driven
  dispatch via a LegacyArgs struct so none of that logic had to change.
- Fixed a real bug found while porting: GenerateKey's `count` used
  short='c', colliding with the global `--config` short (also 'c'), which
  clap validates across the whole command tree on first parse() -- a
  duplicate short flag there could break every subcommand's parsing, not
  just generate-key's.
- `update` now takes `-b/--branch` and `-v/--version` explicitly (was a
  bare flag with no way to target a channel or exact version).
- Added the UAC elevation step for TUN mode that this lineage's CLI was
  completely missing (`run_client_directly` went straight to creating the
  TUN adapter unelevated). Also fixed the elevation check itself: it only
  tested `ret <= 32`, but ShellExecuteW returns ERROR_CANCELLED (1223) when
  the user clicks "No" on the UAC prompt -- > 32, so a denied prompt was
  read as success and the process exited silently without starting the
  tunnel. Now ret==1223 is reported explicitly, and a genuine failure logs
  GetLastError() so the real Win32 cause is visible next time.
- scripts/install.sh: added -b/--branch alongside the existing -v/--version,
  and channel-aware release resolution (nightly/pre-release use their own
  rolling tag; stable resolves via the GitHub API's "latest").
- release.yml: added nightly/pre-release branch-push triggers for rolling
  prereleases. Fixed the tag_name logic from 5c2b5a0, which mapped
  `master` pushes to a release tagged "nightly" -- backwards from master
  being the most stable channel. github.ref_name already equals the
  branch or tag name that triggered the run, so no per-branch remapping is
  needed at all; master is intentionally left off the branch-push list --
  it only ever gets real version tags.

Verified: `cargo build -p ostp` succeeds, and `ostp update --help` /
`ostp generate-key --help` / `ostp --help` show the expected flags with no
clap panic.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-08 01:38:44 +03:00
ospab acab38c551 0.4.1: per-key junk marker, GUI polish, pre-release pipeline
security / protocol:
- Derive a PER-KEY junk marker (obfuscation.rs, info byte 0x04) instead of the
  global constant [0x88,0x1A,0x93,0x5D]. A fixed marker was a universal DPI
  signature identifying ALL OSTP users at once — exactly what the HKDF version
  gate avoids for the handshake. Server drops junk via a new DispatchOutcome::Junk
  inside the existing key-trial loop (secrets already derived → zero extra cost);
  client stamps its own key's marker.
- §E: configurable junk/fragmentation params (junk_pc / junk_ps / frag_chunk / frag_sleep).

GUI (desktop):
- Light theme + toggle, GUI version footer in Settings.
- Fix mouse-wheel scroll on Settings (flex child needed min-height: 0).
- Drop the false "process exclusions unsupported in TUN mode" warning — they DO
  work (native_handler maps port->process via GetExtendedTcpTable).

release / infra:
- build.ps1: add -PreRelease (tag CURRENT version as v<ver>-beta.N, no bump, no
  master commit); guard the panel build when ostp-control ships no source; bump
  the real ostp-gui/package.json instead of the nonexistent ostp-control one.
- release.yml: mark hyphenated tags as GitHub pre-releases; don't hard-fail the
  web-panel step when there is no source (use committed dist/).
- Versions aligned to 0.4.1; README license badge BSL 1.1 -> AGPL v3.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 16:33:57 +03:00
ospab db65e3367f §E (base): port junk packets + TCP fragmentation (stream-only)
Anti-DPI obfuscation the project wants to keep, ported from 0.3.x with the
harmful UDP behaviour designed out from the start.

- Junk: before the handshake on a UoT/TCP connection, send 2-5 random
  length-prefixed frames (100-1000 B). The server reads each as a frame,
  fails to authenticate it, drops it and keeps reading (drop-and-continue),
  so junk perturbs DPI flow analysis without breaking the connection. Junk
  is NEVER sent over UDP — there each junk would be a lone datagram
  indistinguishable from a port scan (probe-flood / wasted CPU / the very
  "self-ban" risk the plan calls out). Verified the server has no
  probe-based ban, and the unauthorized-probe log is already rate-limited
  (§B), so junk-over-UoT produces one debug line, not a flood.
- TCP fragmentation: new `transport.tcp_fragmentation` flag (default off).
  When set, the writer splits the first real frame (the handshake) — length
  header byte-by-byte then payload in 2-byte chunks with short gaps — so DPI
  can't classify the handshake from a single read.
- Ranges are hardcoded for now; §E fine-tuning (configurable Jc/Jmin/Jmax,
  S1/S2, H1..H4) is deferred.

Verified by loopback E2E: a UoT client with tcp_fragmentation=true connects
(junk logged as one rate-limited probe, then real handshake accepted) and
curl via SOCKS5 tunnels HTTPS successfully.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 17:11:12 +03:00
ospab 38f2d9e659 §B: port stability fixes from 0.3.x onto the clean base
Ported only the fixes that actually apply to the pre-refactor base
(the handshake fixes 6eb7b36/d65af35 fix bugs the 0.3.1 multi-server
refactor introduced into the new outbounds/ostp.rs; the base bridge.rs
already waits for the handshake response with retransmit + NAT64
fallback, so they are intentionally skipped).

- EMFILE (922cf0b): rlimit::increase_nofile_limit at CLI startup.
- Logs (1151726): UoT connect/disconnect → debug; rate-limit the
  unauthorized-probe log to one line / ~30s so a junk/probe flood can't
  spam the log (and a client running junk-over-UDP can't self-ban).
- Helper lifecycle + bypass routes (b6e78c1):
  * ostp-tun-helper forces std::process::exit(0) after run_server so the
    WinTun adapter and its metric-0 default route are reclaimed instead
    of lingering as a zombie that breaks the next connect.
  * windows_route: delete_routes_for_dest() purges stale /32s, dedupe
    bypass IPs, and log add failures at warn!.
  * windows: retry tun::create through the transient ERROR_INVALID_
    PARAMETER window and widen the adapter-index lookup to ~15s.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 16:30:42 +03:00
ospab 92d6b06d75 §A: remove WSS + Reality (TLS-mimicry); bump to 0.4.0 / AGPL-3.0
Clean-rebuild on the stably-working v0.2.98 base. The project's stealth
path is zapret-like (packet obfuscation / junk / fragmentation), NOT
TLS-mimicry, so WSS and Reality are dropped entirely.

- Delete dead orphan files: ostp-client/src/transport/xhttp.rs and
  ostp-core/src/crypto/reality.rs (never declared as modules → not even
  compiled), plus ostp-core/src/framing/wss.rs.
- Scrub the `wss` transport field from client config/bridge, the unified
  CLI (ostp/src/main.rs), the Tauri GUI backend, the GUI frontend
  (index.html/main.js), and the Flutter UI; also drop the Reality
  pbk/sid plumbing and XTLS auto-search modes from both frontends.
- Drop now-unused client deps (x25519-dalek, chacha20poly1305, hex).
- Bump workspace to version 0.4.0 and license AGPL-3.0; make ostp's
  ostp-core dep path-only so the version bump resolves.
- gitignore ostp-control/ (panel assets built separately; a dummy dist
  is created for the rust-embed build).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 16:29:42 +03:00
ospab 486d745d47 feat(tun): implement process bypass for TCP/UDP and IP bypass for UDP using existing Extended tables 2026-06-14 00:02:08 +03:00
ospab 55215567dd Fix all compilation errors and suppress all warnings across workspace 2026-06-13 02:30:57 +03:00
ospab 2a24ac34d0 Remove Reality/XTLS from all UI components and TSX pages (Dashboard, Settings, Tools) 2026-06-13 02:19:53 +03:00
ospab a9e4511190 Fix CLI setup permissions, enforce global debug tracing, and fix GUI silent startup crash 2026-06-13 01:25:54 +03:00
ospab fbf13b86f3 Fix syntax and type errors after DNS removal 2026-06-10 22:59:10 +03:00
ospab 9f35caf4ca Remove built-in DNS server and owndns features 2026-06-10 22:52:35 +03:00
ospab 7bb7d211fa Remove stealth_port entirely and integrate fallback into UoT HTTP handler 2026-06-10 02:26:13 +03:00
ospab 04c31c7f53 feat: implement wintun dynamic downloading, add missing driver frontend modal, fix background logging and UAC helper issues 2026-06-09 01:01:36 +03:00
ospab 730eab8553 feat: implement built-in DNS server, adblock and dns leak prevention 2026-06-07 19:55:42 +03:00
ospab ba5fe72873 feat(cli): add --import, --proxy-env, interactive link prompt, and TUN safety guard for Linux 2026-05-31 20:53:54 +03:00
ospab 7656f3a3ce feat: implement custom Reality protocol with ChaCha20Poly1305 and X25519 2026-05-29 15:00:17 +03:00
ospab f4830f043f feat: implement optional WSS framing for DPI bypass & extract framing logic 2026-05-29 13:59:59 +03:00
ospab 19f2c36400 Fix STUN bug, improve DNS in TUN, fix config gen, add GHA for clients 2026-05-28 14:39:42 +03:00
ospab 5c39f24bee fix(server): return API token support for Relay servers sync 2026-05-28 01:28:29 +03:00
ospab 38c4f242e4 fix: include owndns and transport type in --links output 2026-05-28 00:28:54 +03:00
ospab cea8ebaa5c feat: Built-in DNS Server with AdBlock and DoH proxy 2026-05-27 22:23:06 +03:00
ospab 9ac0908c1e fix(server): generate correct public IP for client configs instead of 0.0.0.0 2026-05-27 18:17:11 +03:00
ospab 49c3bce029 fix: config migration uses hardcoded field injection, no ostp --init; fix init template api fields 2026-05-26 21:59:41 +03:00
ospab 7f499d6263 feat: embed web panel via rust-embed with login page and custom webpath 2026-05-26 21:30:49 +03:00
ospab 9c59cabfc7 fix: ostp --update uses correct install URL; api returns name in user list 2026-05-26 20:24:33 +03:00
ospab 46c1ac4519 feat: add --uninstall and --update CLI commands 2026-05-26 20:05:23 +03:00
ospab cba7be4b75 Implement config management API, token generation, and update wiki 2026-05-26 19:33:45 +03:00
ospab d79b6f2384 feat: relay node system with HMAC pre-validation and key sync from upstream API 2026-05-26 16:29:23 +03:00
ospab ed3196be2e Generate Reality keys upon --init server 2026-05-25 22:20:39 +03:00
ospab 4dfe5fd3ca Fix ostp link generator for reality and uot 2026-05-24 23:14:44 +03:00
ospab 3e511f1fc5 Implement XTLS-Reality masquerade for UoT/TCP and fix MTU/config settings 2026-05-24 22:49:51 +03:00
ospab b87e87a7bd fix: correctly parse transport config section from json in CLI and GUI 2026-05-21 02:33:14 +03:00
ospab ceb760e4ce feat: implement server-side UoT and MTU tuning 2026-05-21 02:23:49 +03:00
ospab 83f7ff2119 feat: UoT and xHTTP stealth 2026-05-21 02:11:02 +03:00
ospab 9329bcef45 feat: WSS transport mode selector in Flutter UI + TransportConfig in Rust 2026-05-21 00:39:12 +03:00
ospab 3a4b5a8c63 chore: fix cargo clippy warnings
- Boxed HandshakeState in NoiseSession to reduce enum variant sizes
- Used is_ok() instead of let Ok(_) pattern
- Applied automatic clippy fixes for minor warnings
2026-05-17 22:22:39 +03:00
ospab 05583e189e feat: v0.2.0 — BBR congestion control, 0-RTT session resumption, management REST API, fallback server, multi-listener
Architecture:
- BBR-inspired congestion controller (SlowStart/ProbeBandwidth/ProbeRTT phases)
- 0-RTT session resumption with anti-replay ticket validation
- Management REST API (axum): /api/users CRUD, /api/server/status, Bearer auth
- TCP fallback proxy for anti-DPI camouflage (nginx/caddy passthrough)
- Multi-listener: bind to multiple UDP addresses simultaneously
- Per-user traffic stats with atomic counters and limit enforcement

Code quality:
- Structured logging: 0 eprintln in server/core/client, all tracing::{info,debug,warn,error}
- 35 unit tests across congestion, resumption, relay, outbound, obfuscation
- Removed dead code: kex.rs, unused dependencies (async-trait, x25519-dalek, rand_distr)
- Modular server: api.rs, fallback.rs, outbound.rs, relay.rs extracted from monolithic lib.rs

CLI:
- --check: config validation
- --generate-key: secure key generation (hex/base64, batch)
- --links: share link generation from server config
- --init: fallback section in server template

Documentation:
- README rewritten with architecture diagram, API examples, CLI reference
- Wiki: Management-API (EN+RU), Configuration (EN+RU), Home (EN+RU) updated
2026-05-17 21:05:44 +03:00
ospab 8eb3fc72cb polish: professionalize all user-facing log output and UX
- Unified log prefix to [ostp] across all modules (was [OSTP Core],
  [ostp-server], [ostp-client], [client], [bridge])
- Removed informal/casual phrasing from all user-visible messages
- Startup messages are clean and concise (mode, server, status)
- Error messages are actionable without being alarming
- Essential server logs (client connect/disconnect) always visible
- Essential client logs (connection status, errors) always visible
- TUN tunnel messages consistent across Windows and Linux
- Removed noisy eprintln from UDP reader hot path
- Status format: [ostp] Status: Connected (rtt=12.3ms)
2026-05-17 03:26:15 +03:00