Skip to content

Commit fe82dea

Browse files
authored
Add ts dev proxy: a local MITM dev proxy serving production hostnames from staging (#798)
* Add ts dev proxy design spec and implementation plan * Add trusted-server-cli crate skeleton with ts dev proxy CLI surface * Add rewrite core with rule matching and header outcomes * Harden rewrite rule matching, port parsing, and test coverage - Compare r.from case-insensitively in RuleTable::first_match to enforce the lowercase invariant regardless of how Rule.from was built - Reject trailing-colon inputs (empty port string) as RuleError::Port in Authority::parse; add rejects_empty_or_missing_port test - Assert scheme_is_tls in rewrite_default_preserves_from_host_and_sets_sni_to_to and rewrite_host_uses_to_authority_with_port * Resolve proxy args and env into a concrete rule table and settings * Drop environment-variable support from dev proxy CLI * Drop env-var support from dev proxy spec and plan * Report I/O failure distinctly when --basic-auth-file cannot be read Add ConfigError::BasicAuthFile variant with a path-carrying display message so file-not-found and permission errors are no longer reported as the misleading "--basic-auth must be USER:PASS" format error. The read failure now maps to BasicAuthFile; only parse failures map to BasicAuth. Includes a unit test that asserts the correct variant is returned for a missing file. * Add per-machine local CA with leaf minting and caching * Create CA key file with mode 0600 from first byte Replace the write-then-chmod pattern in CertAuthority::persist() with a single OpenOptions::create_new(true).mode(0o600) open, eliminating the window where the private key was briefly world/group-readable on disk. * Add CONNECT MITM proxy server with blind tunnel and local PAC route * Fix blind-HTTP head replay and add 403 guard test for off-loopback CONNECT - Thread the raw buffered bytes through `RequestHead` so `blind_forward_http` can write the complete request head to the upstream before piping the rest of the socket bidirectionally (spec §8.4). Previously the head was discarded, sending a truncated/empty request. - Update `blind_forward_http` doc comment to reflect that it now replays the original head rather than falsely claiming it always did. - Add `unmatched_connect_off_loopback_is_refused_with_403` integration test. The proxy listener is bound on `127.0.0.1:0` (real socket) but `cfg.listen` is patched to `0.0.0.0:<port>` before being handed to `serve_on`, so `is_loopback` is computed as false while the test can still connect via loopback. Asserts that an unmatched `CONNECT` receives `403` and no tunnel is established. * Format trusted-server-cli with rustfmt * Add browser orchestration, PAC generation, and ca trust subcommands * Persist and recover Safari system proxy state across hard kills Replace the dead `std::thread::park()` restore thread in `launch_safari` with a file-based persist-and-recover scheme. Before applying the PAC URL, write `<ca_dir>/safari-proxy-restore` capturing the network service name and the prior auto-proxy URL (or an empty second line when auto-proxy was off). Add `restore_system_proxy_if_pending(ca_dir)`, which reads and deletes that file then runs the appropriate `networksetup` command to put things back. Wire it into `run()` in two places: at startup (crash recovery from a previous hard-killed run) and in a `tokio::select!` on `ctrl_c()` (clean exit). Also move the function-local `use std::time::{SystemTime, UNIX_EPOCH}` out of `make_temp_dir` to the top-level imports per project convention. * Infer dev-proxy rule from trusted-server.toml for zero-arg use * Document ts dev proxy setup, trust, and troubleshooting * Format dev proxy spec and plan with Prettier * Commit trusted-server-cli lockfile with resolved dependencies * Require explicit rewrite rule; drop trusted-server.toml inference * Update spec, plan, and guide: rules are explicit, no toml inference * Change default proxy port to 18080 to avoid 8080 collisions * Open Safari at the FROM URL on --launch safari, like Chrome and Firefox * Note Safari opens the FROM URL in the plan * Fix Safari network-service detection and elevate networksetup with sudo * Address dev-proxy review: Safari restore, PAC route, per-request Host, header hygiene * Document Safari restore state/interactivity and explicit-rule order * Harden Safari recovery, CA partial state, regenerate trust, Host match, and service detection * Abort Safari config without restore record, confirm CA revocation, shell-quote restore URL * Add native CI job for the dev-proxy CLI crate and document regenerate trust revocation * Address dev-proxy review round 4: macOS-only gate, regenerate abort, FROM validation - Abort `ca regenerate` when the old CA's keychain revocation can't be confirmed, so on-disk key material never outlives its OS trust. - Declare the CLI macOS-only via `compile_error!` on non-macOS targets (keychain, Safari, and networksetup are all macOS-specific), and gate the macOS-only helpers (`manual_restore_command`, `restore_auto_proxy`) while ungating `shell_quote` so the shared launch path compiles cleanly. - Shell-quote the keychain, cert, and profile paths in the `ca install` and Firefox `certutil` fallback instructions. - Validate rule FROM as a bare hostname before embedding it in the generated PAC, browser URL, and upstream Host header. - Ignore the workspace-excluded crate's `target/` directory. Spec, plan, and guide updated to match. * Scope dev-proxy deps to macOS so wasm builds fail with the clear message The macOS-only `compile_error!` lived inside the proxy module while all native deps stayed unconditional, so a build for the repo-default wasm32-wasip1 target failed first in tokio/ring/aws-lc-sys instead of with the intended "macOS only" error — an easy developer footgun (a plain `cargo check` in the crate inherits the wasm default from .cargo/config.toml). - Move every dependency (and dev-dependency) under `[target.'cfg(target_os = "macos")'.dependencies]` so unsupported targets build none of the native TLS/networking stack. - Lift the platform gate to the crate root: `compile_error!` in lib.rs and `#[cfg(target_os = "macos")]` on the command modules, the CLI types, and the binary entry point. The proxy module's own `compile_error!` is removed. - Gate the e2e test crate to macOS (its deps are now macOS-scoped). Now `cargo check --target wasm32-wasip1` emits exactly one error — the macOS-only message — with no dependency build attempts. Native build/test unchanged (31 unit + 6 e2e pass). Spec and plan updated to match. * Support IP upstreams via an optional-valued --rewrite-host Let `--to` target a bare IP and `--rewrite-host` carry the hostname that endpoint expects. The flag now takes an optional value: - omitted -> Host = FROM (default; unchanged) - --rewrite-host -> Host = TO host (the prior bare-flag behavior) - --rewrite-host <HOST> -> Host = <HOST> and TLS SNI = <HOST> Connection still dials `--to`, so pointing at an IP works: the proxy presents the explicit hostname for both SNI and Host while the socket goes to the IP. Replaces `Rule.preserve_host: bool` with a `HostMode { PreserveFrom, UseTo, Explicit }` enum threaded through `rewrite_for`; the explicit host is validated as a hostname (new `ConfigError::InvalidRewriteHost`). Spec, plan, and guide updated; adds tests for all three forms plus invalid-value rejection. * Drop the macOS-only compile_error gate; rely on macOS-scoped deps The `compile_error!` fired on a plain `cargo build` (no `--target`), which the repo's `.cargo/config.toml` resolves to `wasm32-wasip1` — so it tripped even on macOS and blocked the natural build command. Remove the gate. The deps and command modules stay scoped to `target_os = "macos"`, so unsupported targets build an empty shell instead of dragging tokio/ring/aws-lc-sys through an unsupported build. Build natively with an explicit `--target` (proper no-`--target` defaulting is separate, edgezero-aligned work). Also fix a stray `x` that slipped into a config test assertion, which broke the lib-test build. Spec and plan updated to match. * Add --resolve DNS pin and simplify --rewrite-host back to a flag Replace the overloaded `--rewrite-host <HOST>` (which set both Host and SNI to reach an IP upstream) with two orthogonal knobs: - `--resolve HOST:IP` (repeatable, curl-style) pins where a hostname's upstream connection dials, leaving the SNI/Host derivation untouched. This is the self-contained way to reach a server by IP while keeping `--to` a hostname so the TLS SNI and certificate stay valid — no /etc/hosts edit. - `--rewrite-host` is a plain bool again: send `Host: TO` instead of `Host: FROM`. The SNI is always the TO host. Drops the `HostMode` enum in favor of `Rule.rewrite_host: bool` named and passed straight from the flag (no `preserve_host` inversion). Adds an e2e proving a non-resolvable TO host still reaches the upstream via --resolve. Spec and guide updated; the guide now leads with the --resolve flow. * Sync dev-proxy plan with --resolve + rewrite_host bool design * Apply --resolve to every upstream connection, log pins at startup The DNS pin only covered the matched-rule MITM upstream, so a CONNECT to a host that isn't a configured --from (a directly-hit host, a sub-resource) still went through the blind tunnel via real DNS and ignored the pin. Route all three connection paths (MITM, blind tunnel, plain-HTTP forward) through a shared `connect_upstream` helper that honors the pin case-insensitively. Also log each pin at startup (`--resolve pin: HOST -> IP`) so it's visible at the default info level — the per-connection summary shows the hostname (SNI/cert use it) even though the socket dials the pinned IP. * Send X-Forwarded-Host: FROM so first-party URLs stay on the production host Trusted Server derives request_host from X-Forwarded-Host (then Host) and anchors all first-party URL rewriting to it. The proxy now always sends X-Forwarded-Host: FROM — standard forward-proxy behavior — so TS emits production-host URLs (tsjs, GPT, DataDome, …) even when --rewrite-host sends Host: TO for an upstream that routes/validates on its own hostname. This decouples routing (Host) from the displayed first-party host: use --rewrite-host freely for host-validating upstreams without skewing the rewritten URLs onto TO. Adds an e2e asserting Host=TO + X-Forwarded-Host=FROM under --rewrite-host. Spec and guide updated (the earlier "avoid --rewrite-host for TS" guidance no longer applies). * Address self-review nits in the dev proxy - rewrite: match the port-stripped host with eq_ignore_ascii_case instead of allocating a lowercased copy per rule (FROM is already stored lowercase). - config: warn when a --resolve HOST matches no rule's TO host (a likely typo whose pin would otherwise silently never apply); still succeeds. Add a test. - config: document why is_valid_host rejects underscores. - browser: correct the Linux chrome_command comment (it doesn't fall back to chromium; the arm is unreached on the macOS-only build anyway). * Strip inbound Forwarded and reconcile X-Forwarded-Host docs Addresses two review findings on the dev proxy (the trusted-host design for --rewrite-host through the real adapter is tracked separately): - server: strip any inbound `Forwarded` before stamping `X-Forwarded-Host`. Trusted Server resolves the request host from `Forwarded` → `X-Forwarded-Host` → `Host`, so a client-supplied `Forwarded` would otherwise outrank the FROM host the proxy injects. Add a unit test. - docs: reconcile stale `X-Orig-Host`-only guidance in the spec/plan — the functional first-party-host header is `X-Forwarded-Host` (TS reads it for request_host); `X-Orig-Host` is an informational duplicate. Note the inbound-`Forwarded` strip in the spec/plan and the `RewriteOutcome` field doc. * Address dev-proxy review: CA-regenerate, credential, scheme, browser fixes - ca regenerate: propagate old cert/key deletion failures (tolerate already-absent) instead of `.ok()`, so a failed delete can't leave the stale key silently in use after printing "regenerated" (P1). - config: reject --basic-auth/--basic-auth-file on a non-loopback --listen; a matched CONNECT is MITM'd even off loopback, so injected credentials would be exposed to any reachable client (P1). - server: stamp an authoritative `X-Forwarded-Proto: https` (the browser leg is always TLS) and drop spoofable `Fastly-SSL`, so `--upstream-plaintext` (or a spoofed header) can't downgrade the first-party scheme (P2). - browser (Firefox): initialise an empty `sql:` NSS DB before `certutil -A`, and import into it, instead of importing against an uninitialised profile DB that fails with SEC_ERROR_BAD_DATABASE (P2). - browser: derive the browser-connect address from `cfg.listen` (normalising wildcard binds to loopback, bracketing IPv6) and use it consistently in Chrome/Firefox/Safari/PAC and the manual hints, instead of hard-coding 127.0.0.1 — so a non-default --listen is honored (P2). Adds unit tests for each. * Address second dev-proxy review round: hop-by-hop, temp dir, CA, platform - server: strip RFC 7230 hop-by-hop request headers and every header named in an inbound `Connection` token before stamping authoritative headers, so a client can't mark `X-Forwarded-Host`/`X-Forwarded-Proto`/`Authorization` as connection-specific and have a downstream intermediary drop them (P2). - browser: create throwaway browser profiles with `tempfile` (random name, 0700, O_EXCL) instead of a predictable timestamped path + `create_dir_all` that a local racer could pre-create; keep the `TempDir` alive until the browser exits (P2). - ca: re-secure existing CA material on the reload path (dir 0700, key 0600) before reusing it, so a drifted/restored group- or world-readable private key is not used indefinitely (P2). - browser: scope `ca_uninstall`'s `find`/`delete-certificate` to the same login keychain `ca_install` trusts into, so uninstall/regenerate act on exactly the trust location this tool manages (P2). - main: on non-macOS the `ts` binary now prints a clear unsupported-platform error and exits nonzero instead of silently succeeding as an empty no-op (P2). Adds unit tests for the header stripping and the CA re-secure-on-reload. * Fix CI: Linux CLI clippy and edgezero dual-source parity - `dev::run` takes `DevCommand` by value; on non-macOS targets the enum is empty, so the by-value parameter is consumed only by an empty match and clippy's `needless_pass_by_value` fires (a reference cannot be used — a zero-arm match is not exhaustive over `&EmptyEnum`). Allow the lint on non-macOS, where the owned value is required. - Point the excluded `trusted-server-integration-tests` crate at edgezero `branch = "main"` to match the root workspace. Mixing `rev = …` (integration crate) with `branch = main` (adapters) made Cargo build edgezero-core twice, breaking the cross-adapter parity test with a type mismatch and leaving the crate's `--locked` Cargo.lock stale. - Reorganize the `.cargo/config.toml` aliases by adapter, sorted within each section, and add `run_cli_macos` / `run_cli_linux` for one-command native runs. * Align dev-proxy webpki-roots to 1.0 to match EdgeZero The dev proxy pulled `webpki-roots 0.26`, but EdgeZero (and the rest of the workspace) resolves `1.0`. That extra 0.26 line in the workspace lockfile does not exist in the excluded integration crate's lockfile, so `check-integration-dependency-versions.sh` flagged a transitive-parity drift. `TLS_SERVER_ROOTS` is API-compatible across the two versions, so bump to `1`, collapsing the duplicate to a single `1.0.8` line shared with the adapters. * Make integration-tests a workspace member; centralize deps in the workspace - Move trusted-server-integration-tests into the workspace. It now shares [workspace.dependencies] (one EdgeZero pin, one lockfile) with the adapters it exercises, so the duplicated pin, the second Cargo.lock, and the check-integration-dependency-versions.sh drift-check (plus its CI wiring) are gone. Being native + Docker-based, it is whitelisted out of the wasm builds. - Hoist every shared/external dependency into [workspace.dependencies] and reference it from each crate via `workspace = true` (preserving per-crate features): the internal crates (js, openrtb), the dev-proxy stack (hyper/rustls/rcgen/…), http-body-util, testcontainers, worker, getrandom, etc. - Sort every [dependencies]/[dev-dependencies] and [workspace.dependencies] section alphabetically. - Switch the Fastly aliases from a `--workspace --exclude <every native crate>` blacklist to a `-p <wasm crates>` whitelist, so adding a native crate no longer requires touching them. - Drop the CLI's unused `reqwest` dev-dependency. * Organize all ts CLI commands under commands/<name>/ Every subcommand's implementation now lives under commands/<name>/, matching the dev command's layout: audit moves to commands/audit/mod.rs, config init to commands/config/init.rs. AuditArgs moves from run.rs into the audit module alongside run_audit, so run.rs only wires subcommands together. audit and config are pub(crate) (crate-internal); dev stays pub for the proxy_e2e integration suite. * Restore spin edgezero.toml; drop now-redundant CLI fmt CI step The spin adapter's tests read edgezero.toml via include_str!("../edgezero.toml"), so it must not be deleted. Also drop the test-cli fmt step: now that the CLI is a workspace member, cargo fmt --all covers it. * Unify edgezero.toml into one multi-adapter manifest Rewrite the root edgezero.toml as a single EdgeZero manifest covering all four adapters (fastly, axum, cloudflare, spin), aligned with edgezero's canonical format: `[app]` with entry = crates/trusted-server-core, one `[adapters.<name>]` block each with real crate/manifest paths, build target/profile/features, and commands (Fastly's preserved verbatim). The locked edgezero schema has no per-adapter store overrides and rejects unknown fields, so stores declare the union of logical ids with `default` set to the primary id. This is safe: nothing in src/ or build.rs reads edgezero.toml — Fastly binds its real stores in fastly.toml, and core resolves store names from trusted-server.toml at runtime. The manifest ids are portable metadata consumed only by `ts config validate` and edgezero deploy tooling. The Spin adapter's regression test now loads this root manifest (include_str!("../../../edgezero.toml")), so the duplicate adapter-spin/edgezero.toml is deleted and spin.toml's watch glob repointed. Validated with `ts config validate` (strict); spin (29) and cli (8) tests pass. * Address PR review feedback for ts dev proxy - Strip hop-by-hop headers from the upstream response (not just the request), so a `Connection: close` cannot tear down the reusable MITM tunnel. - Reject malformed CONNECT cleanly: a non-numeric port or a truncated/oversized request head now returns 400 instead of falling back to 443 or routing a partially-read request. - Add `--connect-timeout` (default 10s) bounding each upstream dial, so a black-holed upstream fails fast into 502 instead of stalling the browser tab. - Cache the rustls client config per verification mode (OnceLock) instead of rebuilding it — and re-parsing the webpki root store — on every request. - `--listen …:0`: bind first and use the OS-assigned port for the PAC and browser launch instead of port 0. - Read the `--basic-auth-file` credential stripping only a trailing newline, so a password's leading/trailing spaces survive. - Shell-quote the Safari manual-setup fallback command. - Docs: `--rewrite-host` no longer claims blanket safety with Trusted Server upstreams — the Fastly/Spin adapters strip `X-Forwarded-Host`, so first-party URLs fall back to `Host: TO`; documented the caveat in the guide and code. - Docs/cleanup: the CLI is a workspace member now (drop the stale per-crate Cargo.lock; fix the guide and the integration-tests README). - edgezero.toml: store ids are the logical `trusted_server_*` names, overridable per adapter/runtime (documented inline). * Address PR review feedback for ts dev proxy - Strip hop-by-hop headers from the upstream response (not just the request), so a `Connection: close` cannot tear down the reusable MITM tunnel. - Reject malformed CONNECT cleanly: a non-numeric port or a truncated/oversized request head now returns 400 instead of falling back to 443 or routing a partially-read request. - Add `--connect-timeout` (default 10s) bounding each upstream dial, so a black-holed upstream fails fast into 502 instead of stalling the browser tab. - Cache the rustls client config per verification mode (OnceLock) instead of rebuilding it — and re-parsing the webpki root store — on every request. - `--listen …:0`: bind first and use the OS-assigned port for the PAC and browser launch instead of port 0. - Read the `--basic-auth-file` credential stripping only a trailing newline, so a password's leading/trailing spaces survive. - Shell-quote the Safari manual-setup fallback command. - Docs: `--rewrite-host` no longer claims blanket safety with Trusted Server upstreams — the Fastly/Spin adapters strip `X-Forwarded-Host`, so first-party URLs fall back to `Host: TO`; documented the caveat in the guide and code. - Docs/cleanup: the CLI is a workspace member now (drop the stale per-crate Cargo.lock; fix the guide and the integration-tests README). - edgezero.toml: store ids are the logical `trusted_server_*` names, overridable per adapter/runtime (documented inline).
1 parent b260260 commit fe82dea

46 files changed

Lines changed: 8481 additions & 5732 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.cargo/config.toml

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,44 +7,54 @@
77
#
88
# All adapters are workspace members so `-p` resolves each.
99
# default-members = [fastly] — required so Viceroy can locate the binary via `cargo run --bin`.
10-
# Use the aliases below to target each adapter with the correct toolchain.
10+
# The aliases below are grouped by adapter; each targets its adapter with the
11+
# correct toolchain (build / check / test / clippy).
1112

1213
[alias]
14+
# Generic: native test with an explicit host target.
1315
test_details = ["test", "--target", "aarch64-apple-darwin"]
14-
test_cli_macos = ["test", "--package", "trusted-server-cli", "--target", "aarch64-apple-darwin"]
15-
build_cli_macos = ["build", "--package", "trusted-server-cli", "--target", "aarch64-apple-darwin"]
16-
test_cli_linux = ["test", "--package", "trusted-server-cli", "--target", "x86_64-unknown-linux-gnu"]
17-
build_cli_linux = ["build", "--package", "trusted-server-cli", "--target", "x86_64-unknown-linux-gnu"]
18-
# Fastly adapter + shared crates (wasm32-wasip1 via Viceroy)
19-
# Excludes Axum (native-only), Cloudflare (wasm32-unknown-unknown), and Spin (separate wasm32-wasip1 job)
20-
test-fastly = ["test", "--workspace", "--exclude", "trusted-server-adapter-axum", "--exclude", "trusted-server-adapter-cloudflare", "--exclude", "trusted-server-adapter-spin", "--target", "wasm32-wasip1"]
21-
# Axum dev server adapter (native)
22-
test-axum = ["test", "-p", "trusted-server-adapter-axum"]
23-
# Cloudflare adapter (native host; WASM target checked separately in CI)
24-
test-cloudflare = ["test", "-p", "trusted-server-adapter-cloudflare"]
25-
# Cloudflare adapter WASM target check (wasm32-unknown-unknown requires --features cloudflare)
26-
check-cloudflare = ["check", "-p", "trusted-server-adapter-cloudflare", "--target", "wasm32-unknown-unknown", "--features", "cloudflare"]
27-
# Spin adapter (native host tests; WASM target checked separately with --features spin)
28-
test-spin = ["test", "-p", "trusted-server-adapter-spin"]
29-
check-spin = ["check", "-p", "trusted-server-adapter-spin", "--target", "wasm32-wasip1", "--features", "spin"]
3016

31-
# Clippy — target-matched to avoid cross-target compile failures
32-
clippy-fastly = ["clippy", "--workspace", "--exclude", "trusted-server-adapter-axum", "--exclude", "trusted-server-adapter-cloudflare", "--exclude", "trusted-server-adapter-spin", "--all-targets", "--all-features", "--target", "wasm32-wasip1", "--", "-D", "warnings"]
17+
# --- Fastly adapter (wasm32-wasip1, run via Viceroy) ---
18+
# Whitelist the wasm-buildable crates (the Fastly adapter + the shared crates it
19+
# builds/tests on wasm) rather than blacklisting every native crate — so adding a
20+
# native crate needs no change here. Axum (native), Cloudflare
21+
# (wasm32-unknown-unknown), Spin, the CLI (native), and integration-tests
22+
# (native) are simply not listed.
23+
build-fastly = ["build", "-p", "trusted-server-core", "-p", "trusted-server-adapter-fastly", "-p", "trusted-server-js", "-p", "trusted-server-openrtb", "--target", "wasm32-wasip1"]
24+
check-fastly = ["check", "-p", "trusted-server-core", "-p", "trusted-server-adapter-fastly", "-p", "trusted-server-js", "-p", "trusted-server-openrtb", "--target", "wasm32-wasip1"]
25+
clippy-fastly = ["clippy", "-p", "trusted-server-core", "-p", "trusted-server-adapter-fastly", "-p", "trusted-server-js", "-p", "trusted-server-openrtb", "--all-targets", "--all-features", "--target", "wasm32-wasip1", "--", "-D", "warnings"]
26+
test-fastly = ["test", "-p", "trusted-server-core", "-p", "trusted-server-adapter-fastly", "-p", "trusted-server-js", "-p", "trusted-server-openrtb", "--target", "wasm32-wasip1"]
27+
28+
# --- Axum adapter (native dev server) ---
29+
build-axum = ["build", "-p", "trusted-server-adapter-axum"]
30+
check-axum = ["check", "-p", "trusted-server-adapter-axum"]
3331
clippy-axum = ["clippy", "-p", "trusted-server-adapter-axum", "--all-targets", "--all-features", "--", "-D", "warnings"]
32+
test-axum = ["test", "-p", "trusted-server-adapter-axum"]
33+
34+
# --- Cloudflare adapter (native host + wasm32-unknown-unknown) ---
35+
# Build/check target the WASM runtime (requires the `cloudflare` feature);
36+
# tests and clippy run on the native host.
37+
build-cloudflare = ["build", "-p", "trusted-server-adapter-cloudflare", "--target", "wasm32-unknown-unknown", "--features", "cloudflare"]
38+
check-cloudflare = ["check", "-p", "trusted-server-adapter-cloudflare", "--target", "wasm32-unknown-unknown", "--features", "cloudflare"]
3439
# No --all-features: the `cloudflare` feature has a compile_error! guard on
3540
# non-wasm32 targets. WASM-feature coverage comes from `cargo check-cloudflare`.
3641
clippy-cloudflare = ["clippy", "-p", "trusted-server-adapter-cloudflare", "--all-targets", "--", "-D", "warnings"]
42+
test-cloudflare = ["test", "-p", "trusted-server-adapter-cloudflare"]
43+
44+
# --- Spin adapter (native host tests + wasm32-wasip1 target) ---
45+
check-spin = ["check", "-p", "trusted-server-adapter-spin", "--target", "wasm32-wasip1", "--features", "spin"]
3746
clippy-spin-native = ["clippy", "-p", "trusted-server-adapter-spin", "--all-targets", "--", "-D", "warnings"]
3847
clippy-spin-wasm = ["clippy", "-p", "trusted-server-adapter-spin", "--target", "wasm32-wasip1", "--features", "spin", "--lib", "--", "-D", "warnings"]
48+
test-spin = ["test", "-p", "trusted-server-adapter-spin"]
3949

40-
# Build — target-matched, same split as test/clippy
41-
build-fastly = ["build", "--workspace", "--exclude", "trusted-server-adapter-axum", "--exclude", "trusted-server-adapter-cloudflare", "--exclude", "trusted-server-adapter-spin", "--target", "wasm32-wasip1"]
42-
build-axum = ["build", "-p", "trusted-server-adapter-axum"]
43-
build-cloudflare = ["build", "-p", "trusted-server-adapter-cloudflare", "--target", "wasm32-unknown-unknown", "--features", "cloudflare"]
44-
45-
# Check — fast compile validation, same split
46-
check-fastly = ["check", "--workspace", "--exclude", "trusted-server-adapter-axum", "--exclude", "trusted-server-adapter-cloudflare", "--exclude", "trusted-server-adapter-spin", "--target", "wasm32-wasip1"]
47-
check-axum = ["check", "-p", "trusted-server-adapter-axum"]
50+
# --- ts operator CLI (native host; per-OS target overrides the wasm default) ---
51+
build_cli_linux = ["build", "--package", "trusted-server-cli", "--target", "x86_64-unknown-linux-gnu"]
52+
build_cli_macos = ["build", "--package", "trusted-server-cli", "--target", "aarch64-apple-darwin"]
53+
run_cli_linux = ["run", "--package", "trusted-server-cli", "--target", "x86_64-unknown-linux-gnu", "--"]
54+
run_cli_macos = ["run", "--package", "trusted-server-cli", "--target", "aarch64-apple-darwin", "--"]
55+
test_cli_linux = ["test", "--package", "trusted-server-cli", "--target", "x86_64-unknown-linux-gnu"]
56+
test_cli_macos = ["test", "--package", "trusted-server-cli", "--target", "aarch64-apple-darwin"]
4857

58+
# When a wasm binary IS built, run it under Viceroy.
4959
[target.'cfg(all(target_arch = "wasm32"))']
5060
runner = "viceroy run -C ../../fastly.toml -- "

.github/actions/setup-integration-test-env/action.yml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@ inputs:
55
origin-port:
66
description: Fixed origin port baked into the WASM binary for integration tests.
77
required: true
8-
check-dependency-versions:
9-
description: Run the integration dependency version consistency check.
10-
required: false
11-
default: "true"
128
install-viceroy:
139
description: Install the Viceroy binary used by integration tests.
1410
required: false
@@ -38,11 +34,6 @@ outputs:
3834
runs:
3935
using: composite
4036
steps:
41-
- name: Check shared dependency versions
42-
if: ${{ inputs.check-dependency-versions == 'true' }}
43-
shell: bash
44-
run: ./scripts/check-integration-dependency-versions.sh
45-
4637
- name: Retrieve Rust version
4738
id: rust-version
4839
shell: bash

.github/workflows/integration-tests.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ jobs:
6868
uses: ./.github/actions/setup-integration-test-env
6969
with:
7070
origin-port: ${{ env.ORIGIN_PORT }}
71-
check-dependency-versions: "false"
7271
install-viceroy: "true"
7372
build-wasm: "false"
7473
build-axum: "false"
@@ -128,7 +127,6 @@ jobs:
128127
uses: ./.github/actions/setup-integration-test-env
129128
with:
130129
origin-port: ${{ env.ORIGIN_PORT }}
131-
check-dependency-versions: "false"
132130
install-viceroy: "true"
133131
build-wasm: "false"
134132
build-test-images: "false"
@@ -172,7 +170,6 @@ jobs:
172170
uses: ./.github/actions/setup-integration-test-env
173171
with:
174172
origin-port: ${{ env.ORIGIN_PORT }}
175-
check-dependency-versions: "false"
176173
install-viceroy: "true"
177174
build-wasm: "false"
178175
build-test-images: "false"

.github/workflows/test.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,41 @@ jobs:
178178
- name: Clippy (parity test crate)
179179
run: cargo clippy --manifest-path crates/trusted-server-integration-tests/Cargo.toml --all-targets -- -D warnings
180180

181+
test-cli:
182+
# `trusted-server-cli` is a workspace member, but the workspace default target
183+
# is wasm32-wasip1 (see .cargo/config.toml), where the crate is an empty shell
184+
# — so the wasm workspace jobs do not exercise its real code. `ts dev proxy` is
185+
# a macOS tool (Safari/keychain/networksetup), so validate the crate on macOS
186+
# with an explicit native --target that overrides the wasm default.
187+
name: cargo test (ts CLI, native)
188+
runs-on: macos-latest
189+
steps:
190+
- uses: actions/checkout@v4
191+
192+
- name: Retrieve Rust version
193+
id: rust-version
194+
run: echo "rust-version=$(grep '^rust ' .tool-versions | awk '{print $2}')" >> $GITHUB_OUTPUT
195+
shell: bash
196+
197+
- name: Set up Rust toolchain
198+
uses: actions-rust-lang/setup-rust-toolchain@v1
199+
with:
200+
toolchain: ${{ steps.rust-version.outputs.rust-version }}
201+
components: "clippy, rustfmt"
202+
cache-shared-key: cargo-cli-${{ runner.os }}
203+
204+
# No separate `cargo fmt` here: `trusted-server-cli` is a workspace member,
205+
# so the main `cargo fmt --all` job already formats it. Clippy still needs a
206+
# native run — the workspace clippy jobs are wasm/adapter-scoped and never
207+
# lint the CLI's host-only code (macOS dev proxy vs Linux audit differ by cfg).
208+
- name: cargo clippy
209+
run: |
210+
cargo clippy --manifest-path crates/trusted-server-cli/Cargo.toml --target "$(rustc -vV | sed -n 's/host: //p')" --all-targets -- -D warnings
211+
212+
- name: cargo test
213+
run: |
214+
cargo test --manifest-path crates/trusted-server-cli/Cargo.toml --target "$(rustc -vV | sed -n 's/host: //p')"
215+
181216
test-typescript:
182217
name: vitest
183218
runs-on: ubuntu-latest

0 commit comments

Comments
 (0)