Commit Graph

5 Commits

Author SHA1 Message Date
ospab d170b6de73 fix(migrate): backfill outbound SOCKS5 credential fields
migrate_server_json backfilled the `api` section but not the newer
outbound.username / outbound.password (added with SOCKS5 upstream auth), so a
server config predating them reported "nothing to migrate" while the fields were
absent. Now backfilled to their "" (no-auth) default.

Deliberately does NOT inject the optional bind_ip (top level) or per-rule
send_from: absent means "use the default source", which is correct and concise,
and a placeholder would be either stripped (null) or, worse, parse as an invalid
source IP (""). Those stay hand-added when the operator wants multi-address
egress. Test covers both the backfill and the no-inject.
2026-08-25 10:13:30 +03:00
ospab 8907f506c7 feat(migrate): normalize any config to a clean, canonical, lossless form
`ostp migrate` now finishes every kind (client/server/relay) with a uniform
normalization pass so a messy config.json becomes a clean one:

- Concise: null-valued keys are stripped at every nesting level. A JSON null
  means "unset", so it is noise; removing it never loses real data (a set value
  is never null). Empty [] / {} are kept — they carry intent.
- Canonical order: free. serde_json serializes object keys sorted, so any
  rewrite comes out stably ordered regardless of how disordered the input was.
- No data loss: normalization works on the JSON value and only removes nulls, so
  fields the schema has never heard of survive verbatim — proven by a test.

This also fixes the "configs stay old even after ostp migrate" complaint: the
normalize pass flips report.changed when it removes anything, so a config that
was current-but-noisy actually gets rewritten clean instead of "nothing to
migrate".

Adds a forcing function: a test that the exact shapes `ostp init` / the wizard
emit (client, server, relay — including the new outbound username/password) are
already canonical, so migrate is a no-op on them. If a template or the schema
gains a field without the migrator being taught, this fails instead of shipping
a config that `ostp migrate` keeps trying to "fix". Plus tests for strip/keep,
idempotency, and unknown-field preservation.
2026-08-24 15:40:07 +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 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