Repository navigation
Add ts config migrate for settings written for main - #1409
Open
jwrosewell wants to merge 30 commits into
Open
jwrosewell wants to merge 30 commits into
jwrosewell wants to merge 30 commits into
Conversation
…nfig commands validate_source_config parses a document as TrustedServerAppConfig and runs the push-time validation, reporting only the byte offset where reading failed or the phase that refused, because a refusal can quote a value. ts config migrate checks the document it writes the same way, so the function and its module are pub(crate). Nothing else changes.
…the shape the stack reads The command reads one file, rewrites every shape the stack refuses or reads differently, and prints the result on stdout. --write rewrites the file in place, and --check changes nothing and exits 1 when the document needs migrating. On stderr it prints a line for each change, for each item the document cannot load without, which needs a decision or a value the document does not hold, and for each change in behavior no setting expresses, then whether the result passes the settings validation ts config validate runs. No line prints a value from the document. The rewrite uses toml_edit, so the comments, the order of the tables and the formatting the operator wrote survive, a module that was not enabled is commented out under its new name, and migrating a migrated document changes nothing. The rules follow the table in docs/superpowers/specs/2026-10-17-config-migrate-design.md. The tests cover each rule and each item left to do, the order of the page entries against trusted-server.example.toml, comments and table order, Windows line endings, a document of marker values no line may print, and the command's three modes. The golden test migrates main's own example, copied into tests/fixtures with its commit recorded, and the result validates under the stack's settings code and migrates again to itself. The 43 migrate tests and the 7 audit validation tests pass on Windows, and clippy with warnings denied and cargo fmt pass.
The command index in docs/guide/cli.md gains the command, and a section on upgrading says to run it first, what it prints and exits with, what it never does, and what to do after it. The changelog gains one line under Added.
…w sections go A profile_config setting named as one of its provider's own is left for the operator, a module with no settings that was not enabled keeps only the comments above its old table, and the sections that replace nothing follow [publisher] and [ec], as the command does.
The check for the [integration] table ran only after [integrations] was found, so a document with [integration] alone was not told it is not read. It runs first now. An [auction] mediator left beside the mock ad server's table, because [ad-server] is written too, is reported with that table and no longer also as a mediator whose settings the document lacks. New tests leave a profile_config setting named as one of its provider's own, both forms of every setting the command refuses to guess between, and [integration] alone, so each item the design lists as left to do has one. The 46 migrate tests and the 7 audit validation tests pass on Windows, and clippy with warnings denied and cargo fmt pass.
…h follow main Three checks, about x-ts-version, Flight responses and the x-geo-* headers, are printed for a document written for main, and the others with the rewrite they follow from. A middleware name is among the values the command renames.
A test parses the command with its file and --check, and refuses it with both modes or with no file, which also runs clap's checks of the new arguments. The 54 argument tests, the 46 migrate tests and the 7 audit validation tests pass on Windows, and clippy with warnings denied and cargo fmt pass.
jwrosewell
added this pull request to stack #1408
October 17, 2026 09:20
…is closed Main matched a handler's pattern anywhere in the path, so a handler on ^/admin/keys also guarded publisher paths such as /admin/keystore, which this release forwards to the origin, and the rule removed it with the password it put on them. A pattern that starts ^/admin/keys is now removed only where /, $, (?:/|$) or (/|$) follows the prefix, because only /admin/keys and the paths below /admin/keys/ are closed. Every path that starts /_ts/admin is closed, so those patterns are removed as before. Any other handler is left to do, and its line names the paths this release closes. The test a_handler_is_removed_only_where_every_path_it_matches_is_closed removes the bounded forms and keeps ^/admin/keys, ^/admin/keys.* and the other patterns that can match a path the stack forwards.
With host_header set, the page pipeline and every middleware take that host as the page's origin host, so a page's addresses under it are rewritten where main rewrote those under the host of origin_url. Moving origin_host_header_override to [source.origin] host_header now prints a check line that says so. The test a_host_header_override_is_reported_to_check covers a document with the override and one without it.
Main ran every enabled integration's page changes on every page, so a document written for main gets the [[fetch]] and [[serve]] entries that keep its pages as they were. A document already in this release's shape loads with no entry, with a warning naming each middleware no entry names, so the rule no longer switches on page changes its operator left off. The entries a module's file needs, on /ads.txt and /robots.txt, are still added to any document, because the file's module refuses a document without one. The test a_document_in_the_stack_shape_with_no_page_entries_gets_none_added selects a consent module in a document already in this release's shape.
The command takes --adapter, being fastly, cloudflare, axum or spin, as ts config push and ts config diff do, because what main's responses carried and what each adapter accepts differ by adapter. A document written for main no longer gets [headers] modules = ["geo"]. headers.geo writes the reader's location in the x-geo-* headers, and main wrote those on Fastly alone, with only x-geo-info-available on Cloudflare, Axum and Spin, so selecting it started sending the reader's location on Cloudflare, where main sent none. Whether pages carry the reader's location is the operator's decision, so a check line now says what main's responses carried on the adapter and, on Fastly, how to keep the headers. The test no_location_header_is_selected_for_the_operator runs on every adapter, the test that no line carries a value runs on every adapter, and the argument test needs --adapter and refuses an adapter the stack does not have.
… one The Axum dev server and Spin refuse [geo] module = "platform" at startup, because neither host has a location lookup, and main's lookups there located no request, so a document written for main gets no geo module on those adapters. Its Edge Cookie module, made from main's passphrase, then needs [geo] assume_single_jurisdiction = true or a geo module, which startup checks and ts config validate does not, so that choice is left to do. The command now reports, for any document and on every run until it is fixed, an Edge Cookie module with no geo module and no acknowledgment, and [geo] module = "platform" on an adapter whose host has no lookup. The test the_location_lookup_is_the_one_each_adapter_accepts runs core's startup check of the acknowledgment on the result for each adapter, and the_location_settings_startup_refuses_are_left_to_do_in_any_document covers both items in documents already in this release's shape.
…w round 1 The design answers each finding of review round 1 and ends with a review log. It gains sections on the adapters, consent and privacy, failure, and compatibility and rollout, says what the command's validation leaves out and which startup checks it reports as items left to do, defines a to do item to cover a setting the stack ignores whose value the operator carries elsewhere, gives the seam probe and the key-value store rows of their own, and counts 39 breaking entries, as the changelog holds. The CLI guide and the changelog entry name --adapter and the location lookup each adapter gets, and say that no location header is selected. The guide asks for a copy of the original before --write, and its steps after migrating rename the overlay variables and keep the key-value store on Cloudflare and Spin.
…or bound
Main matched a handler's pattern anywhere in the path, and a ?, * or {
straight after the ^/_ts/admin prefix or after the /admin/keys bound can
let the character or the bound before it match nothing. ^/admin/keys/?,
^/admin/keys/* and ^/admin/keys(?:/|$)? all matched /admin/keystore, and
^/_ts/admin? matched /_ts/admit, paths the stack forwards to the origin,
yet the command removed each such entry with a changed: line and no to
do: item. It now leaves such an entry to do. A + is still accepted,
because it matches what comes before it at least once.
The test a_handler_is_removed_only_where_every_path_it_matches_is_closed
runs each pattern through the regex crate main matched with and through
core's closed_path_response on a set of request paths, so each pattern
the command removes is shown to guard no path the stack forwards, and
each it keeps as unsafe is shown to guard one. It keeps the forms review
round 2 named, the other quantifier forms, and ^/admin/keys/?$, which
the command reads by its text and leaves to do although every path it
matches is closed.
…refix The scan for a | outside a group ended a character class at the first ] after a [, but the regex crate main matched with reads a ] at the start of a class as a character, a [ inside a class as a nested class, and, with the (?x) flag, a # as the start of a comment. A ( hidden that way raised the scan's depth, so a later | looked as if it were inside a group, and ^/_ts/admin[](]|^/members, which guarded /members on main, was removed. The command now leaves to do any pattern with a [ or a # after the prefix or the bound, and the scan reads only escapes and groups. The test a_handler_with_a_class_or_comment_after_its_prefix_is_left_to_do shows through the regex crate that three such patterns guarded /members on main, and that ^/_ts/admin/[a-z]+ is left to do although every path it matches is closed. The test helper now writes each pattern as a TOML string, so a pattern can hold a newline.
Main's Cloudflare lookup set no region, so its US state rules never applied there. The stack's lookup reads the region from cf-region-code, where Cloudflare sends it, so once a document written for main selects [geo] module = "platform" on Cloudflare, a reader in a US state the permissions.yaml rules list takes that state's us-state jurisdiction, where the country gives non-regulated. The geo rewrite on Cloudflare now prints a check line that says so. The test the_region_the_cloudflare_lookup_now_reads_is_reported_to_check runs on every adapter and finds the line on Cloudflare alone.
…ew round 2 The design answers each finding of review round 2 and adds its entry to the review log, whose round 1 entry now names the public branch that held that commit. It states the handler rule the code now follows, with the quantifier, class and comment forms it leaves to do, names the checks made only at startup, says the consent rules see the location main's jurisdiction check saw, adds the region check line on Cloudflare, says a document run on two adapters is migrated once for each, and no longer calls this the top pull request of the stack. The CLI guide adds the step on stored pages and stored assets being fetched again, and says a configuration pushed to more than one adapter is migrated once for each. The comment on the report's items left to do gives the definition the design and the guide use.
jwrosewell
added this pull request to stack #1418
October 19, 2026 12:18
jwrosewell
removed this pull request from stack #1418
October 19, 2026 16:30
jwrosewell
added this pull request to stack #1421
October 19, 2026 17:38
jwrosewell
removed this pull request from stack #1421
October 19, 2026 18:29
jwrosewell
added this pull request to stack #1422
October 19, 2026 19:20
This was referenced Oct 19, 2026
jwrosewell
marked this pull request as ready for review
October 19, 2026 20:24
13 of 14 tasks
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Changes
crates/trusted-server-cli/srccrates/trusted-server-cli/testsdocs/superpowersdocs/guideCHANGELOG.mdCloses
Part of #777
Spec
Spec: config-migrate
Test plan
CI on this pull request at a71edd1 passed all 27 checks.
cargo test-fastly && cargo test-axumcargo clippy-fastly && cargo clippy-axumcargo fmt --all -- --checkcd crates/trusted-server-js/lib && npx vitest runcd crates/trusted-server-js/lib && npm run formatcd docs && npm run formatcargo build --package trusted-server-adapter-fastly --release --target wasm32-wasip1fastly compute serveChecklist
unwrap()in production code (clippy deniesunwrap_usedin CI)logmacros as AGENTS.md asks (notprintln!)Stacks on cookies by path (#1400).
What this pull request does
The design is config-migrate. Its section 2.2 lists every breaking entry in the changelog that concerns the document and how the command handles it, and its section 6 gives the steps for moving a deployment.
ts config migrate --adapter <adapter> <file>names the host the document is pushed to,fastly,cloudflare,axumorspin, because the location lookup a document can select differs by host.Rewritten without a question:
[integrations.<id>]becomes the module in the section for its type, with its settings at[<section>.<name>], so[integrations.prebid]becomes[auction.prebid]and[integrations.gpt]becomes[ad-tag.google]. A module that was not enabled is kept as a commented block under its new name.[auction.providers.<id>]becomes[demand] modulesand a[demand.<id>]table, withprofileasimplementationand theprofile_configsettings in the table, and a bidder route'sproviderbecomesmodule.[ad-server] module = "iabtl"and[ad-server.iabtl], andinclude_mediator_responsebecomesinclude_adserver_response.[publisher]origin settings become[source] module = "origin"and[source.origin], and each asset route's origin moves into itssourcetable.[tinybird]becomes[analytics] module = "tinybird"and[analytics.tinybird].[ec.hmac], the permission signal sources to[permission-signal] modules, and[device] module = "fastly"becomes"platform".[[fetch]]and[[serve]]entries a module's page change now needs are added fortext/html, in the ordertrusted-server.example.tomlshows, with the entries[robots-txt]and[ads-txt]need. Only a document written formaingains them, because a document already in the stack's shape loads without them.main,[geo] module = "platform"is added on Fastly and Cloudflare, wheremainran the host's lookup. Axum and Spin have no lookup, so no geo module is added there.check:line says whatmain's responses carried on that adapter and, on Fastly, how to keep thex-geo-*headers.Left for the operator, with the setting in place and the command exiting 1:
[[handlers]]entry unless every path its pattern matches is one the stack closes, so a pattern with a quantifier, a class or a comment after^/_ts/admin, or after the^/admin/keysbound, stays[consent.gdpr]and[consent.us_states], an unknown integration id, and settings that would clash after the move[geo] assume_single_jurisdiction = trueor a geo module before the stack startsReported as
check:lines, which leave the exit status alone: a host header override that moves, and on Cloudflare the region the lookup now reads, so US state rules can apply there.Why
The stack's changelog lists 39 breaking entries under Unreleased, where
mainlists 10, and a settings document written formaindoes not load under the stack. The repository has no release and no tag, so no released version breaks, and with this command no operator rewrites a document by hand.How it was verified
Locally on Windows, at b6a9c1e: the CLI's library suite passed, 706 tests with 9 ignored, 54 of them the command's, clippy on the CLI with warnings denied and
cargo fmt --all -- --checkpassed, and Prettier passed on the changed Markdown. Each commit of the change passes clippy and the command's tests on its own. The golden test migratesmain's owntrusted-server.example.toml, copied from 5bc3886, and the result passes the stack's validation, and migrating it again changes nothing. The handler tests run every pattern through theregexcratemainlocks, on 16 request paths, against the paths core closes, so a removed handler guarded no path the stack forwards. Each new test fails with its change reverted. CI runs the CLI's full suite on Linux and macOS.