Skip to content

Add ts dev proxy: a local MITM dev proxy serving production hostnames from staging#798

Merged
aram356 merged 60 commits into
mainfrom
worktree-review-ts-dev-proxy-spec
Jul 2, 2026
Merged

Add ts dev proxy: a local MITM dev proxy serving production hostnames from staging#798
aram356 merged 60 commits into
mainfrom
worktree-review-ts-dev-proxy-spec

Conversation

@aram356

@aram356 aram356 commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

Closes #824

Summary

Adds ts dev proxy — a local TLS-terminating (MITM) developer proxy that serves a production publisher hostname from a dev/staging upstream by swapping the TLS SNI. A real browser shows the production domain in the address bar while a Compute/staging service answers, so you can exercise Trusted Server's host-anchored URL rewriting against real first-party behavior without DNS or /etc/hosts changes.

This PR started as the design spec + implementation plan and now contains the full implementation (new trusted-server-cli crate, CA, MITM server, browser/PAC orchestration, e2e tests, docs). ~9k lines across 22 files.

What's included

New ts developer CLI (crates/trusted-server-cli, a native binary excluded from the wasm workspace):

ts dev proxy --from www.example-publisher.com --to staging.example.net --launch chrome
ts dev proxy ca install | uninstall | regenerate

Proxy flags

  • --map FROM=TO (repeatable) or -f/--from + -t/--to HOST[:PORT] — the rewrite rule(s); rules are explicit (no trusted-server.toml inference).
  • --rewrite-host — send Host: TO upstream (default keeps Host: FROM so core's URL rewriting stays anchored to the production host).
  • --resolve HOST:IP (repeatable) — curl-style DNS pin; dials the given IP on every upstream path while keeping SNI/Host on the hostname, so the cert still validates. Makes the proxy self-contained (no hosts-file edits).
  • --basic-auth USER:PASS / --basic-auth-file PATH — inject Authorization when absent (clears a gated upstream).
  • --listen ADDR (default 127.0.0.1:18080), --launch chrome|firefox|safari, --insecure (accept self-signed upstreams), --allow-non-loopback.

Example

# Serve the production host from a staging upstream, pinning the upstream
# connection to a specific staging IP (no DNS / /etc/hosts changes), injecting
# Basic auth, and opening Chrome at the production URL:
ts dev proxy \
  --from www.example-publisher.com \
  --to   staging.example.net \
  --resolve staging.example.net:192.0.2.10 \
  --rewrite-host \
  --basic-auth "$DEV_USER:$DEV_PASS" \
  --launch chrome

--resolve staging.example.net:192.0.2.10 dials 192.0.2.10 for the upstream
leg while leaving the TLS SNI and Host as staging.example.net, so the
upstream certificate still validates — the curl --resolve model. Repeat the
flag to pin multiple hosts. IPv6 targets work too (the value is split on the
first :), e.g. --resolve staging.example.net:2001:db8::10.

Architecture

  • CONNECT MITM: matched hosts get a freshly minted leaf (SNI→TO) signed by a per-machine local CA; the decrypted stream is proxied request-by-request over keep-alive. Unmatched hosts are blind-tunnelled on loopback (never MITM'd), and a Host matching no rule is refused 421 (no smuggling through the CONNECT-authority rule).
  • Per-machine CA (ca install adds it to the login keychain; uninstall/regenerate revoke prior trust before replacing key material).
  • Browser + PAC orchestration: HTTPS-only proxying via a served /proxy.pac route; Chrome/Firefox/Safari launch helpers (Safari persists & restores system-proxy state across hard kills).
  • X-Forwarded-Host/X-Orig-Host = FROM so the upstream can keep emitting first-party URLs on the production host even when --rewrite-host sends Host: TO.

Safety

  • Binds loopback-only by default; off-loopback unmatched CONNECT/forward is refused 403 (can't become an open proxy) unless --allow-non-loopback.
  • macOS-only: dependencies are cfg-scoped to macOS, so non-macOS targets compile to an empty shell with a clear message instead of a hard compile_error! (which previously broke cargo build under the repo's wasm default target).

Tests, CI, docs

  • ~43 unit + e2e tests (tests/proxy_e2e.rs): MITM rewrite/forward, blind tunnel cert identity, --resolve pinning, --rewrite-host Forwarded-Host behavior, injected Basic auth, keep-alive multi-request, 421/403 guards.
  • New native CI job for the CLI crate (built with an explicit host target, since the repo pins wasm32-wasip1 by default).
  • docs/guide/ts-dev-proxy.md (setup, trust, troubleshooting) + the design spec and implementation plan under docs/superpowers/.

Notes

  • The spec/plan went through several review rounds; resolved items are recorded in-document.

aram356 added 30 commits June 22, 2026 13:21
- 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
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.
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.
…NNECT

- 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.
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.
…-spec' into worktree-review-ts-dev-proxy-spec
@aram356 aram356 marked this pull request as ready for review June 29, 2026 06:40

@ChristianPavilonis ChristianPavilonis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: I reviewed the new trusted-server-cli dev proxy implementation, CA/browser orchestration, CI wiring, and the related docs. CI is currently green and I did not find a blocking issue that requires requesting changes, but I found several correctness/security edge cases that should be addressed before relying on this tool broadly.

Comment thread crates/trusted-server-cli/src/commands/dev/proxy/mod.rs Outdated
Comment thread crates/trusted-server-cli/src/commands/dev/proxy/server.rs
Comment thread crates/trusted-server-cli/src/commands/dev/proxy/server.rs
Comment thread crates/trusted-server-cli/src/commands/dev/proxy/browser.rs
Comment thread crates/trusted-server-cli/src/commands/dev/proxy/browser.rs Outdated
aram356 added 2 commits June 30, 2026 13:38
…v-proxy-spec

# Conflicts:
#	.github/workflows/test.yml
#	.gitignore
…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.
@aram356

aram356 commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks for the review — all five findings addressed in 1f529a1ba:

  • P1 (ca regenerate silent reuse) — old cert/key deletions now propagate failures (already-absent tolerated) and the success message only prints after the files were actually removed; a failed delete aborts instead of reloading the stale pair.
  • P1 (creds on non-loopback)config::resolve now rejects --basic-auth/--basic-auth-file on a non-loopback --listen (BasicAuthNonLoopback), since a matched CONNECT is MITM'd off loopback too.
  • P2 (plaintext scheme downgrade)rewrite_headers stamps an authoritative X-Forwarded-Proto: https (browser leg is always TLS) and strips spoofable Fastly-SSL; inbound Forwarded was already stripped.
  • P2 (Firefox NSS DB) — initialise an empty sql: DB (certutil -N --empty-password -d sql:<profile>) before importing, and import with -d sql:<profile>; manual hint updated to both commands.
  • P2 (listen address ignored) — added proxy_connect_addr/proxy_connect_host (wildcard→loopback, IPv6 bracketed) and used them in Chrome/Firefox/Safari/PAC + manual hints, so a non-default --listen is honored.

Unit tests added for each; CLI crate fmt/clippy/test green.

@ChristianPavilonis ChristianPavilonis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: I reviewed the current head of PR #798 (new trusted-server-cli dev proxy, CA/browser orchestration, tests/CI, and docs) against main. CI is green and I did not find a blocking correctness/security issue requiring REQUEST_CHANGES, but I found several medium-risk hardening/correctness issues worth addressing before broad use of this MITM dev tool.

Comment thread crates/trusted-server-cli/src/commands/dev/proxy/server.rs Outdated
Comment thread crates/trusted-server-cli/src/commands/dev/proxy/browser.rs Outdated
Comment thread crates/trusted-server-cli/src/commands/dev/proxy/ca.rs
Comment thread crates/trusted-server-cli/src/commands/dev/proxy/browser.rs Outdated
Comment thread crates/trusted-server-cli/src/main.rs
aram356 added 11 commits June 30, 2026 23:10
…form

- 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.
Both branches introduced crate `trusted-server-cli`: main's `ts` operator CLI
(auth/build/config/deploy/provision/serve, delegated to `edgezero-cli`) and this
branch's macOS `ts dev proxy`. Reconcile them into one crate.

- The dev proxy is now `ts dev proxy`, a `dev` subcommand of the unified CLI.
  `dev` is available on every host target; only its `proxy` subcommand (and the
  heavy TLS/networking deps behind it) is scoped to macOS, so `DevCommand` is an
  empty enum on other hosts.
- Adopt main's entry point (`run_from_env`, `edgezero_cli::init_cli_logger`) and
  structure (workspace member, `[lints] workspace = true`). The proxy code now
  satisfies the shared workspace lints.
- Move the crate's test-only lint allowances into the root `clippy.toml`
  (add `allow-print-in-tests` / `allow-dbg-in-tests`) instead of a per-crate
  `#![cfg_attr(test, allow(...))]` block.
- Track edgezero on its `main` branch instead of a pinned rev.
…-spec' into worktree-review-ts-dev-proxy-spec
- `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.
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.
…kspace

- 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.
…v-proxy-spec

# Conflicts:
#	Cargo.lock
#	Cargo.toml
#	crates/trusted-server-cli/src/run.rs
#	crates/trusted-server-integration-tests/Cargo.lock
#	crates/trusted-server-integration-tests/Cargo.toml
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.
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.
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.

@prk-Jr prk-Jr left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

ts dev proxy is a well-built, security-conscious addition. I read the full proxy surface (server, rewrite, config, ca, browser, mod, run), the e2e suite, module gating, dependency scoping, and CI wiring. The audit/config changes are pure moves (96–100% similarity — import-path + rustfmt only, no logic change). No blocking issues found; approving. The notes below are all optional follow-ups.

What stands out (👍)

  • Security posture: loopback-only default with 403 off-loopback refusal, 421 misdirected-request guard against Host smuggling over a MITM tunnel, hop-by-hop + inbound Forwarded stripping so the injected X-Forwarded-Host wins, authoritative X-Forwarded-Proto: https + Fastly-SSL drop, refusing --basic-auth on a non-loopback bind, 0600 CA key with re-secure-on-reload, and ca regenerate aborting if OS trust cannot first be revoked.
  • Detail: byte-at-a-time head read so the TLS ClientHello is never consumed before the CONNECT 200; IP-literal hosts get an IP-type SAN.
  • Test + CI design: 43 unit/e2e tests including the blind-tunnel cert-identity assertion (proves an unmatched host is not MITM'd), IPv6 --resolve, keep-alive multi-request, and the 421/403 guards; macOS-native CI exercises the proxy while the Linux-native job covers audit and the non-macOS fallback arms.

Non-blocking notes

♻️ refactor / perf

  • Per-request TLS client configcrates/trusted-server-cli/src/commands/dev/proxy/server.rs:461 (proxy_to_upstream) rebuilds a rustls::ClientConfig and re-parses the whole webpki_roots store on every upstream hop; the service closure at server.rs:341 also clones rules/basic_auth/resolve per request. Build the client config once (Arc in ResolvedConfig or a OnceLock) and share the immutable inputs. Dev-tool scale, so not urgent.

🌱 seedling

  • No upstream timeoutconnect_upstream (server.rs:212) has no connect/read timeout; a black-holed upstream (easy with a wrong --resolve pin) stalls the browser tab until the OS TCP timeout. A tokio::time::timeout around dial + handshake would fail fast into the existing 502 path.
  • No upstream connection reuse — a fresh TCP + TLS handshake per request even within one keep-alive client tunnel (server.rs:438). Acknowledged tradeoff; noting for a future pass.

🤔 thinking

  • Concurrent Safari instances — two concurrent ts dev proxy --launch safari runs collide: the second's startup restore_system_proxy_if_pending reverts the first's live system PAC and deletes its restore file (browser.rs:456). Single-instance is an implicit assumption; worth a doc note or a lockfile guard.

⛏ nitpick

  • Credential trimmingresolve_basic_auth (config.rs:305) trims the whole value/file; correct for the trailing newline but it also strips a password's meaningful leading/trailing whitespace. Trimming only trailing \r/\n is safer.
  • is_valid_host leniency — accepts leading/trailing . and .. (config.rs:208). Safe for the PAC/URL/Host interpolation contexts (the stated purpose), just not a strict hostname.

CI Status

All checks green at time of review (fmt, clippy across adapters, rust tests incl. ts-CLI native + cross-adapter parity, vitest, docs format).

Comment thread crates/trusted-server-cli/src/commands/dev/proxy/server.rs
Comment thread crates/trusted-server-cli/src/commands/dev/proxy/server.rs
Comment thread crates/trusted-server-cli/src/commands/dev/proxy/config.rs Outdated

@ChristianPavilonis ChristianPavilonis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

📝 Approved with comments. I reviewed the current PR #798 diff against main, including the new ts dev proxy, CA/browser orchestration, workspace/EdgeZero manifest changes, CI, and docs. CI is green and I did not find any P0 blockers.

I left inline notes using the CREG guide: 🔧 for changes worth addressing before broad use, and 🌱 for lower-risk follow-ups/hardening.

Comment thread crates/trusted-server-cli/src/commands/dev/proxy/server.rs
Comment thread edgezero.toml
Comment thread crates/trusted-server-cli/src/commands/dev/proxy/mod.rs Outdated
Comment thread crates/trusted-server-cli/src/commands/dev/proxy/server.rs
Comment thread crates/trusted-server-cli/src/commands/dev/proxy/server.rs Outdated
Comment thread crates/trusted-server-cli/src/commands/dev/proxy/browser.rs
Comment thread docs/guide/ts-dev-proxy.md Outdated
aram356 added 2 commits July 2, 2026 13:14
- 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).
- 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).
@aram356 aram356 merged commit fe82dea into main Jul 2, 2026
16 checks passed
@aram356 aram356 deleted the worktree-review-ts-dev-proxy-spec branch July 2, 2026 20:22
prk-Jr added a commit that referenced this pull request Jul 7, 2026
Reconcile the ad-template CLI + generate feature onto the commands/
module layout the base branch adopted from main (ts dev proxy, #798):

- audit/ -> commands/audit/ : mod.rs is the audit namespace; the #800
  draft-gen plus slot reconstruction move under commands/audit/generate/
  (base's duplicate commands/audit/{analyzer,browser_collector} dropped).
- config_ad_templates.rs -> commands/config/ad_templates.rs.
- app_config and ad_templates support modules stay at crate root.
- run.rs/lib.rs wiring merged with the new `ts dev` command.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add ts dev proxy: local MITM dev proxy for serving production hostnames from staging

3 participants