Repository navigation
The publisher origin can be a folder of signed object storage - #1377
Draft
jwrosewell wants to merge 14 commits into
Draft
jwrosewell wants to merge 14 commits into
jwrosewell wants to merge 14 commits into
Conversation
A publisher whose site is static files in object storage had to be served by an asset route, which skips the page pipeline. Three optional [publisher] settings let the ordinary publisher proxy serve it, so its pages pass through the fetch and serve middleware. Each is off unless set, and a document without them sends the origin request it sent before and serializes to the same bytes. A path in origin_url is kept, and the request path is added after it with one slash. With a path set, a request path with a dot segment in any spelling is refused with 400, so no request reaches outside the folder. origin_auth takes the table an asset route's auth takes and signs through the same function. Only GET and HEAD are signed and any other method is refused with 405, so the publisher's credentials are only used to read. A signed request carries none of the reader's cookies, credentials, referrer, conditional validators or range, and none of the signing namespace's headers. Its key references resolve from the secret store as an asset route's do. directory_index names a file added to a request path ending in a slash, for the origin request only. A malformed origin_auth or directory_index is refused when the settings load, with the setting named. The asset route refusals keep their wording.
…proven masked The design page for the publisher origin in object storage follows the shape of the stack's design documents, and the changelog and the guide's list of static secret references name the new settings. The configuration view's test of every declared secret now holds the three key references of publisher.origin_auth, which it requires of any declared secret, so they are proven masked in the view and absent from what it serves. A further test keeps them out of debug output. The comments on the signer and on the auth type say the publisher origin uses them too.
# Conflicts: # CHANGELOG.md
The publisher origin check and the closed path check share one bounded percent-decoding helper. A closed path still decoding after the last round is now closed, as a dot segment already was.
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-core/srcdocs/superpowersdocs/guidetrusted-server.example.tomlCHANGELOG.mdCloses
Closes #1329
Test plan
CI: 19 of 19 checks green, the fork's CI on f5f8ac2 (jwrosewell#31).
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!)On top of a deployment addressing a request by its path (#30). Issue: #1329.
What this adds
What changes.
A publisher whose site is static files in object storage is served by the ordinary publisher proxy, so its pages pass through the fetch and serve middleware, the template cache and the making of a missing file, all of which an asset route skips. Three optional
[publisher]settings do it, each off unless set.//bucket/site/index.html, signed/news//bucket/site/news/index.html, signed/news/bucket/site/news, signed/style.css?v=2/bucket/site/style.css?v=2, signed/news/../../other-site/page400POST, or any method butGETandHEAD405withAllow: GET, HEADHow it works.
origin_urlis kept and the request path is added after it with exactly one/, whichever side carries one. The query is kept. Anorigin_urlwith no path sends the request path as it arrived, which is what every existing document does. The address is built in one place, so the streamed page, the buffered page, the template cache key, aHEADand every other method take the same one.origin_authis the table an asset route'sauthtakes, read as the same type with the same fields, defaults and secret store key references, and it signs through the same function, so there is one signing path. The request is signed last, once its address andHostare final. OnlyGETandHEADare signed and any other method is answered405, so the publisher's credentials are only used to read. A signed request carries none of the reader'sCookie,Authorization,Proxy-Authorization,Referer, conditional validators orRange, and none of the signing scheme's own headers.origin_urlmust usehttpswhenorigin_authis set.origin_url, a request path with a.or..segment, as written or after up to four rounds of percent-decoding, is answered400and nothing is sent.directory_indexnames one file, added to a request path ending in/for the origin request only. The reader's address and the path the middleware see do not change.origin_auth, anorigin_authbeside anorigin_urlthat is nothttps, an empty key reference (byts config validateandts config push), and adirectory_indexthat is not one file name.Why
The page pipeline, the template cache, the making of a file the origin does not have and the cache policy for a path all hang off the publisher path. A second path for a site kept in storage would carry each of them again, and a page would behave differently according to where its bytes are kept. Each of the three settings changes the request core already makes to the one origin
[publisher]names, none is a vendor's, and the signer is the one core already has for asset routes.What breaks
Nothing for a document that sets none of the three. It sends the origin request it sent before and serializes to the same bytes. A document that sets one is refused by a build that predates them, because
[publisher]refuses a field it does not know, so the build is deployed before the document is pushed.Publishergains two public fields,origin_authanddirectory_index, so Rust code outside the crate that buildsPublisherwith a struct literal stops compiling until it adds them or uses..Default::default().The commits
origin_auth,s3_sigv4andREADER_HEADERS_NOT_SIGNEDCI being read on f5f8ac2.
What is not in this pull request
403, and with404only where the credentials may also list the bucket. No storage service was called in testing this change.The design page is
docs/superpowers/specs/2026-10-13-publisher-object-storage-origin-design.md.