From e548440e178b576fb0c85ecac1223c7bfc7d9e2d Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Mon, 14 Sep 2026 13:37:22 -0700 Subject: [PATCH 1/8] Add a published new-engineer onboarding page The documentation refresh moves onboarding to docs/internal/ and adds internal/** to srcExclude, so the page is no longer published. It was also never in the sidebar, and neither README nor CONTRIBUTING links to it, which leaves a new engineer without a discoverable entry point. Add docs/guide/onboarding-new-engineer.md as a published page and link it from the Introduction group so it is reachable from the site. It covers what the reference guides assume rather than repeating them: what the system does, the request path by function name, the ad-tech vocabulary as used in this codebase, where the code lives, a first week, and a symptom-to-page troubleshooting table. The internal maintainer page is left untouched. Co-Authored-By: Claude Opus 5 (1M context) --- docs/.vitepress/config.mts | 4 + docs/guide/onboarding-new-engineer.md | 149 ++++++++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 docs/guide/onboarding-new-engineer.md diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 422085179..851564d05 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -93,6 +93,10 @@ export default withMermaid( text: 'Onboarding', link: '/guide/onboarding', }, + { + text: 'New Engineer Setup', + link: '/guide/onboarding-new-engineer', + }, ], }, { diff --git a/docs/guide/onboarding-new-engineer.md b/docs/guide/onboarding-new-engineer.md new file mode 100644 index 000000000..456f932fb --- /dev/null +++ b/docs/guide/onboarding-new-engineer.md @@ -0,0 +1,149 @@ +# New Engineer Onboarding + +This page orients a new engineer on the Trusted Server codebase. It answers the +questions the reference guides assume you already know: what the system does, +how one request flows through it, what the ad-tech vocabulary means here, and +what to do in your first week. + +Read [What is Trusted Server?](/guide/what-is-trusted-server) for the product +framing and [Getting Started](/guide/getting-started) for setup. This page is +the bridge between them and the rest of the documentation. + +## What the system does + +A publisher puts Trusted Server in front of their site at the CDN edge. Every +page request passes through it before reaching the publisher's origin. That +position lets Trusted Server do three things a page script cannot: + +1. **Serve advertising infrastructure as first-party.** Third-party ad and + identity scripts are proxied through the publisher's own domain, so they are + not blocked as cross-site requests and do not depend on third-party cookies. +2. **Generate and hold identity at the edge.** The Edge Cookie (EC) ID is + derived server-side rather than written by browser JavaScript. +3. **Run the ad auction before the page is sent.** Bids can be collected while + the origin response is still being fetched, so the auction does not have to + wait for the browser to parse the page. + +The rest of the system exists to make those three things safe: consent +enforcement, creative sanitization, request signing, and per-integration +rewriting of the publisher's HTML. + +## The request path + +This is the single most useful thing to understand. A publisher page request +travels roughly this route. Line numbers drift; use the function names. + +| Step | Where | What happens | +| ---- | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | +| 1 | `adapter-fastly/src/main.rs` — `main` | `/health` short-circuits before anything else loads, then `edgezero_main` runs | +| 2 | `main.rs` — `edgezero_main` | Client IP is resolved and sanitized; trusted TLS headers are re-injected; client and device signals are captured | +| 3 | `adapter-fastly/src/app.rs` — `build_ec_request_state` | Device signals, bot gate, geo, and the EC context are assembled | +| 4 | `app.rs` — `run_pre_route_filters` | Integration request filters run before routing | +| 5 | `core/src/publisher.rs` — `handle_publisher_request` | The main publisher path | +| 6 | `core/src/creative_opportunities.rs` — `evaluate_ad_stack_gate` | Decides whether the ad stack runs at all for this request | +| 7 | `publisher.rs` — auction dispatch | Bids are requested **before** the origin response is sent, while the original client headers are intact | +| 8 | `core/src/html_processor.rs` — `create_html_processor` | One streaming pass rewrites URLs and injects integration scripts | +| 9 | `publisher.rs` | The response is finalized, EC state is written, and the body is streamed | + +Two details in that list surprise people: + +- **The auction starts before the origin responds.** It is not triggered by the + browser. Step 7 happens while step 8 is still waiting on the publisher. +- **The ad-stack gate can turn everything off.** If step 6 declines, no auction + runs and no scripts are injected. When ads are missing, check the gate before + suspecting the auction. + +See [Architecture](/guide/architecture) for the component view and +[Auction Orchestration](/guide/auction-orchestration) for the auction itself. + +## Vocabulary + +The reference guides use these terms without defining them. Here is what each +one means _in this codebase_. + +| Term | Meaning here | +| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Impression** | One opportunity to show one ad in one slot on one page view. | +| **Ad slot** / **placement** | A region of the page that can hold an ad. Identified by a DOM element ID. | +| **Creative** | The actual ad markup that gets rendered — HTML, an image, or a script. Arrives from a bidder and is rendered inside a sandboxed iframe. | +| **Bid** | An offer to buy one impression at a price. | +| **Auction** | Collecting bids for the page's slots and choosing winners. | +| **Header bidding** | Running an auction among several demand sources before calling the ad server, so they compete rather than being asked in a fixed order. | +| **SSP** | Supply-side platform — sells the publisher's inventory. A demand source from our perspective. | +| **DSP** | Demand-side platform — buys on behalf of advertisers, usually via an SSP. | +| **CPM** | Cost per thousand impressions, the usual unit of a bid price. | +| **OpenRTB** | The IAB-standard JSON format for bid requests and responses. Our types live in the `trusted-server-openrtb` crate. | +| **Prebid** | The open-source header-bidding framework. We ship a browser shim plus an optional first-party bundle. | +| **GPT** | Google Publisher Tag, the browser library that requests ads from Google Ad Manager. | +| **GAM** | Google Ad Manager, the ad server that decides what finally renders. | +| **EC ID** | Edge Cookie ID. A privacy-preserving identifier derived at the edge with HMAC-SHA256 rather than written by page JavaScript. See [Edge Cookies](/guide/edge-cookies). | +| **Consent string** | An encoded record of what a user agreed to. TCF covers GDPR, GPP is the newer multi-jurisdiction container, GPC is a browser-level opt-out signal. | +| **CMP** | Consent management platform — the vendor that shows the consent banner and produces the consent string. | +| **First-party proxy** | Serving a third-party asset through the publisher's own domain. See [First-Party Proxy](/guide/first-party-proxy). | + +## Where the code lives + +| Path | What it is | +| ---------------------------------------------- | ---------------------------------------------------------------------------------- | +| `crates/trusted-server-core/` | Nearly all the logic. Start here. | +| `crates/trusted-server-core/src/publisher.rs` | The main request path. Large — navigate by function, not by reading top to bottom. | +| `crates/trusted-server-core/src/auction/` | Auction orchestration, providers, and bidders. | +| `crates/trusted-server-core/src/ec/` | Edge Cookie identity. | +| `crates/trusted-server-core/src/consent/` | Consent parsing and enforcement. | +| `crates/trusted-server-core/src/integrations/` | One module per vendor integration. | +| `crates/trusted-server-js/lib/src/` | The browser-side TypeScript that ships to the page. | +| `crates/trusted-server-adapter-*/` | Per-runtime entry points. The Fastly adapter is production. | +| `crates/trusted-server-cli/` | The `ts` operator CLI. | + +The core crate is runtime-agnostic; anything Fastly-specific belongs in the +adapter. A test enforces that core never imports the Fastly SDK. + +## Your first week + +**Get it running.** Follow [Getting Started](/guide/getting-started). The Axum +dev server is the fastest path and needs no cloud account. + +Three things that are easy to trip over: + +- Bare `cargo build` and `cargo test` fail at the workspace root, because the + adapters target different architectures. Use the aliases in + `.cargo/config.toml` — `cargo test-axum`, `cargo test-fastly`, and so on. A + bare `cargo test` fails while linking with missing `fastly` symbols. +- `ts` is built from this repository. After pulling changes that touch + configuration types, reinstall it with `cargo install-cli`, or + `ts config validate` will reject a valid `trusted-server.toml`. +- The Rust build runs the JavaScript build, so Node from `.tool-versions` is + required even if you are only touching Rust. + +**Read one request end to end.** Follow the table above through the source with +the file open. That single exercise explains more than any other. + +**Make a small change.** Pick something with a test next to it, change it, and +watch the test fail. `cargo test-axum` is the quickest loop. + +**Learn the tooling you will need later.** +[Dev Proxy](/guide/ts-dev-proxy) serves a publisher's real hostname from your +local build, which is the only practical way to reproduce most production +issues. [GPT Diagnostics](/guide/integrations/gpt-diagnostics) explains +`?ts_console=1`, the first thing to reach for when ads do not render. + +## When something does not work + +| Symptom | Look at | +| -------------------------------- | -------------------------------------------------------------------------------------------------- | +| Build or config error | [Error Reference](/guide/error-reference) | +| Tests fail or will not run | [Testing](/guide/testing) — check you used the right alias | +| Ads not rendering | `?ts_console=1` via [GPT Diagnostics](/guide/integrations/gpt-diagnostics); then the ad-stack gate | +| Behaviour differs on a real site | [Dev Proxy](/guide/ts-dev-proxy) | +| Config rejected on push | [Configuration](/guide/configuration) and [CLI](/guide/cli) | + +Consent is worth calling out: if the consent gate closes, there are no bids and +no ads, and the symptom looks like a broken auction rather than a consent +decision. Check consent state before debugging demand. + +## Contributing + +Read [CONTRIBUTING.md](https://github.com/IABTechLab/trusted-server/blob/main/CONTRIBUTING.md) +for the pull-request process and commit-message conventions, and `CLAUDE.md` +for coding standards and the full CI gate list. Run the gates that match what +you changed before opening a pull request. From b4fa1a957bc88fc68c533302a14ff63a182319aa Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Mon, 14 Sep 2026 13:44:53 -0700 Subject: [PATCH 2/8] Reuse the original onboarding content in the new engineer page The page was written from scratch when the onboarding guide added in #183 already covered much of it. Recover that material instead: the problem statement, what edge position buys, the architecture diagram, the first-party proxy before/after, the key-concepts framing, the local origin stub, and the common-issues table. Correct what drifted since #183 rather than copying it forward: - crates/js/lib is now crates/trusted-server-js/lib - the pinned Rust version is read from .tool-versions through the existing {{RUST_VERSION}} substitution instead of being hardcoded - synthetic ID is now the Edge Cookie ID, and the {{ip}} template config it described no longer exists - the `impl Integration for` example no longer matches the registration builder, so it is replaced with a description of the hooks - the architecture diagram gains the other three adapters and the ad-stack gate - the origin stub uses example.com-style hosts, not a real domain Common issues keeps only entries that still reproduce, and adds the three that cost a newcomer the most time today. --- docs/guide/onboarding-new-engineer.md | 267 ++++++++++++++++++-------- 1 file changed, 187 insertions(+), 80 deletions(-) diff --git a/docs/guide/onboarding-new-engineer.md b/docs/guide/onboarding-new-engineer.md index 456f932fb..933700f2c 100644 --- a/docs/guide/onboarding-new-engineer.md +++ b/docs/guide/onboarding-new-engineer.md @@ -1,32 +1,87 @@ # New Engineer Onboarding This page orients a new engineer on the Trusted Server codebase. It answers the -questions the reference guides assume you already know: what the system does, -how one request flows through it, what the ad-tech vocabulary means here, and -what to do in your first week. +questions the reference guides assume you already know: what problem the system +solves, how one request flows through it, what the ad-tech vocabulary means +here, and what to do in your first week. Read [What is Trusted Server?](/guide/what-is-trusted-server) for the product framing and [Getting Started](/guide/getting-started) for setup. This page is the bridge between them and the rest of the documentation. -## What the system does +## Project overview -A publisher puts Trusted Server in front of their site at the CDN edge. Every -page request passes through it before reaching the publisher's origin. That -position lets Trusted Server do three things a page script cannot: +Trusted Server is an open-source edge computing framework from IAB Tech Lab +that moves advertising operations out of third-party browser JavaScript and +into WebAssembly running on edge platforms. -1. **Serve advertising infrastructure as first-party.** Third-party ad and - identity scripts are proxied through the publisher's own domain, so they are - not blocked as cross-site requests and do not depend on third-party cookies. -2. **Generate and hold identity at the edge.** The Edge Cookie (EC) ID is - derived server-side rather than written by browser JavaScript. -3. **Run the ad auction before the page is sent.** Bids can be collected while - the origin response is still being fetched, so the auction does not have to - wait for the browser to parse the page. +### The problem it solves -The rest of the system exists to make those three things safe: consent -enforcement, creative sanitization, request signing, and per-integration -rewriting of the publisher's HTML. +- **Privacy restrictions.** Third-party cookie deprecation and tracking + prevention limit traditional advertising. +- **Third-party dependency.** Publishers have little control over the + third-party scripts running on their pages. +- **Performance.** Multiple third-party scripts slow page load. +- **Data control.** Publishers need control over how and with whom data is + shared. + +### What edge position buys + +- **First-party context.** Ads and assets are served from the publisher's own + domain. +- **Consent enforcement.** Consent is read and enforced before any demand call + is made. +- **Better performance.** Server-side processing reduces client-side + JavaScript. +- **Identity at the edge.** The Edge Cookie (EC) ID is derived server-side + rather than written by page JavaScript. + +## Architecture at a glance + +``` + User's browser + │ + ▼ +┌──────────────────────────────────────────────────────────┐ +│ Edge runtime (adapter + core) │ +│ │ +│ Adapter (Fastly / Cloudflare / Spin / Axum) │ +│ • Entry point, routing, platform bindings │ +│ • Client IP, TLS signals, EC request state │ +│ │ │ +│ ┌────────────────┼────────────────┐ │ +│ ▼ ▼ ▼ │ +│ ┌───────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ Proxy │ │ Publisher │ │ Integrations │ │ +│ │ │ │ │ │ │ │ +│ │ /first- │ │ Origin fetch │ │ Prebid, GPT, │ │ +│ │ party/* │ │ Ad-stack gate│ │ APS, consent │ │ +│ │ Creative │ │ Auction │ │ vendors, ... │ │ +│ │ rewriting │ │ HTML rewrite │ │ │ │ +│ └───────────┘ └──────────────┘ └──────────────┘ │ +│ │ +│ Storage layer │ +│ • KV stores • Config stores • Secret stores │ +└──────────────────────────────────────────────────────────┘ + │ + ┌───────────────┴───────────────┐ + ▼ ▼ + Publisher origin Demand partners +``` + +The core crate is runtime-agnostic; anything platform-specific lives in an +adapter. A test enforces that core never imports the Fastly SDK. + +### Technology stack + +| Layer | Technology | +| -------------- | ---------------------------------------- | +| Language | Rust {{RUST_VERSION}} | +| Runtime | WebAssembly (`wasm32-wasip1` for Fastly) | +| Edge platforms | Fastly Compute, Cloudflare Workers, Spin | +| Dev server | Axum (native) | +| Client library | TypeScript (tsjs) | +| Build tools | Cargo, esbuild | ## The request path @@ -56,69 +111,93 @@ Two details in that list surprise people: See [Architecture](/guide/architecture) for the component view and [Auction Orchestration](/guide/auction-orchestration) for the auction itself. -## Vocabulary +## Key concepts -The reference guides use these terms without defining them. Here is what each -one means _in this codebase_. +### First-party proxying + +Instead of loading ad creatives and vendor scripts directly from third-party +domains, Trusted Server proxies them through first-party endpoints: + +``` +Before: Browser → ad-server.example/creative.html +After: Browser → publisher.example/first-party/proxy?tsurl=... +``` + +Everything stays under the publisher's domain, which avoids third-party cookie +restrictions and tracking prevention. Proxy URLs are signed, so the endpoint +cannot be used as an open relay. See +[First-Party Proxy](/guide/first-party-proxy). + +### Edge Cookie identity + +The EC ID is a privacy-preserving identifier derived at the edge with +HMAC-SHA256 rather than written by page JavaScript. It is deterministic for the +same inputs, non-reversible, and publisher-controlled. Rotating the passphrase +resets the identity graph. See [Edge Cookies](/guide/edge-cookies). + +### Integration modules + +Each vendor lives in its own module under +`crates/trusted-server-core/src/integrations/` and registers the hooks it +needs — proxying, request filtering, attribute rewriting, script rewriting, +HTML post-processing, or head injection. Browser-side counterparts live in +`crates/trusted-server-js/lib/src/integrations/`. See the +[Integration Guide](/guide/integration-guide) before adding one. -| Term | Meaning here | -| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **Impression** | One opportunity to show one ad in one slot on one page view. | -| **Ad slot** / **placement** | A region of the page that can hold an ad. Identified by a DOM element ID. | -| **Creative** | The actual ad markup that gets rendered — HTML, an image, or a script. Arrives from a bidder and is rendered inside a sandboxed iframe. | -| **Bid** | An offer to buy one impression at a price. | -| **Auction** | Collecting bids for the page's slots and choosing winners. | -| **Header bidding** | Running an auction among several demand sources before calling the ad server, so they compete rather than being asked in a fixed order. | -| **SSP** | Supply-side platform — sells the publisher's inventory. A demand source from our perspective. | -| **DSP** | Demand-side platform — buys on behalf of advertisers, usually via an SSP. | -| **CPM** | Cost per thousand impressions, the usual unit of a bid price. | -| **OpenRTB** | The IAB-standard JSON format for bid requests and responses. Our types live in the `trusted-server-openrtb` crate. | -| **Prebid** | The open-source header-bidding framework. We ship a browser shim plus an optional first-party bundle. | -| **GPT** | Google Publisher Tag, the browser library that requests ads from Google Ad Manager. | -| **GAM** | Google Ad Manager, the ad server that decides what finally renders. | -| **EC ID** | Edge Cookie ID. A privacy-preserving identifier derived at the edge with HMAC-SHA256 rather than written by page JavaScript. See [Edge Cookies](/guide/edge-cookies). | -| **Consent string** | An encoded record of what a user agreed to. TCF covers GDPR, GPP is the newer multi-jurisdiction container, GPC is a browser-level opt-out signal. | -| **CMP** | Consent management platform — the vendor that shows the consent banner and produces the consent string. | -| **First-party proxy** | Serving a third-party asset through the publisher's own domain. See [First-Party Proxy](/guide/first-party-proxy). | +### Request signing + +Ed25519 signing authenticates outbound API requests. Public keys are published +at `/.well-known/trusted-server.json`, and rotation is supported with a grace +period. See [Request Signing](/guide/request-signing) and +[Key Rotation](/guide/key-rotation). ## Where the code lives -| Path | What it is | -| ---------------------------------------------- | ---------------------------------------------------------------------------------- | -| `crates/trusted-server-core/` | Nearly all the logic. Start here. | -| `crates/trusted-server-core/src/publisher.rs` | The main request path. Large — navigate by function, not by reading top to bottom. | -| `crates/trusted-server-core/src/auction/` | Auction orchestration, providers, and bidders. | -| `crates/trusted-server-core/src/ec/` | Edge Cookie identity. | -| `crates/trusted-server-core/src/consent/` | Consent parsing and enforcement. | -| `crates/trusted-server-core/src/integrations/` | One module per vendor integration. | -| `crates/trusted-server-js/lib/src/` | The browser-side TypeScript that ships to the page. | -| `crates/trusted-server-adapter-*/` | Per-runtime entry points. The Fastly adapter is production. | -| `crates/trusted-server-cli/` | The `ts` operator CLI. | - -The core crate is runtime-agnostic; anything Fastly-specific belongs in the -adapter. A test enforces that core never imports the Fastly SDK. +| Path | What it is | +| ---------------------------------------------- | ----------------------------------------------------------------------- | +| `crates/trusted-server-core/` | Nearly all the logic. Start here. | +| `crates/trusted-server-core/src/publisher.rs` | The main request path. Large — navigate by function, not top to bottom. | +| `crates/trusted-server-core/src/auction/` | Auction orchestration, providers, and bidders. | +| `crates/trusted-server-core/src/ec/` | Edge Cookie identity. | +| `crates/trusted-server-core/src/consent/` | Consent parsing and enforcement. | +| `crates/trusted-server-core/src/integrations/` | One module per vendor integration. | +| `crates/trusted-server-js/lib/src/` | The browser-side TypeScript that ships to the page. | +| `crates/trusted-server-adapter-*/` | Per-runtime entry points. The Fastly adapter is production. | +| `crates/trusted-server-cli/` | The `ts` operator CLI. | + +## Vocabulary + +The reference guides use these terms without defining them. Here is what each +one means _in this codebase_. + +| Term | Meaning here | +| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Impression** | One opportunity to show one ad in one slot on one page view. | +| **Ad slot** / **placement** | A region of the page that can hold an ad. Identified by a DOM element ID. | +| **Creative** | The actual ad markup that gets rendered — HTML, an image, or a script. Arrives from a bidder and renders inside a sandboxed iframe. | +| **Bid** | An offer to buy one impression at a price. | +| **Auction** | Collecting bids for the page's slots and choosing winners. | +| **Header bidding** | Running an auction among several demand sources before calling the ad server, so they compete rather than being asked in a fixed order. | +| **SSP** | Supply-side platform — sells the publisher's inventory. A demand source from our perspective. | +| **DSP** | Demand-side platform — buys on behalf of advertisers, usually via an SSP. | +| **CPM** | Cost per thousand impressions, the usual unit of a bid price. | +| **OpenRTB** | The IAB-standard JSON format for bid requests and responses. Our types live in the `trusted-server-openrtb` crate. | +| **Prebid** | The open-source header-bidding framework. We ship a browser shim plus an optional first-party bundle. | +| **GPT** | Google Publisher Tag, the browser library that requests ads from Google Ad Manager. | +| **GAM** | Google Ad Manager, the ad server that decides what finally renders. | +| **EC ID** | Edge Cookie ID. See [Edge Cookies](/guide/edge-cookies). | +| **Consent string** | An encoded record of what a user agreed to. TCF covers GDPR, GPP is the newer multi-jurisdiction container, GPC is a browser-level opt-out signal. | +| **CMP** | Consent management platform — the vendor that shows the consent banner and produces the consent string. | ## Your first week **Get it running.** Follow [Getting Started](/guide/getting-started). The Axum dev server is the fastest path and needs no cloud account. -Three things that are easy to trip over: - -- Bare `cargo build` and `cargo test` fail at the workspace root, because the - adapters target different architectures. Use the aliases in - `.cargo/config.toml` — `cargo test-axum`, `cargo test-fastly`, and so on. A - bare `cargo test` fails while linking with missing `fastly` symbols. -- `ts` is built from this repository. After pulling changes that touch - configuration types, reinstall it with `cargo install-cli`, or - `ts config validate` will reject a valid `trusted-server.toml`. -- The Rust build runs the JavaScript build, so Node from `.tool-versions` is - required even if you are only touching Rust. +**Read one request end to end.** Follow the request-path table above through the +source with the files open. That single exercise explains more than any other. -**Read one request end to end.** Follow the table above through the source with -the file open. That single exercise explains more than any other. - -**Make a small change.** Pick something with a test next to it, change it, and +**Make a small change.** Pick something with a test beside it, change it, and watch the test fail. `cargo test-axum` is the quickest loop. **Learn the tooling you will need later.** @@ -127,19 +206,47 @@ local build, which is the only practical way to reproduce most production issues. [GPT Diagnostics](/guide/integrations/gpt-diagnostics) explains `?ts_console=1`, the first thing to reach for when ads do not render. +### Local origin stub + +To exercise the first-party proxy against a fully local origin, point the +publisher origin at a local server and sign an asset URL: + +```bash +# Terminal 1: serve a file from a local origin +export TRUSTED_SERVER__PUBLISHER__ORIGIN_URL=http://localhost:9090 +mkdir -p /tmp/ts-origin +printf 'hello from origin\n' > /tmp/ts-origin/hello.txt +python3 -m http.server 9090 --directory /tmp/ts-origin +``` + +```bash +# Terminal 2: with the server running, sign the URL and fetch it +curl -s "http://127.0.0.1:7676/first-party/sign?url=http://localhost:9090/hello.txt" +``` + +Request the signed path that comes back; the response body should be +`hello from origin`. + +## Common issues + +| Issue | Solution | +| ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | +| Bare `cargo build` / `cargo test` fails | The adapters target different architectures. Use the aliases in `.cargo/config.toml`, such as `cargo test-axum` or `cargo test-fastly`. | +| `ts config validate` rejects a valid config | `ts` is built from this repository. Reinstall it with `cargo install-cli` after pulling changes to configuration types. | +| Rust build fails in the JS step | The Rust build runs the JavaScript build, so Node from `.tool-versions` is required even for Rust-only work. | +| `cargo test-fastly` fails on Viceroy | Install the pinned version: `cargo install viceroy --version {{VICEROY_VERSION}} --locked --force`. | +| Tests pass locally but fail in CI | Run `cargo fmt --all -- --check` and the target-matched clippy aliases; CI denies warnings. | +| No bids and no ads | Check consent state and the ad-stack gate before suspecting demand. | + ## When something does not work -| Symptom | Look at | -| -------------------------------- | -------------------------------------------------------------------------------------------------- | -| Build or config error | [Error Reference](/guide/error-reference) | -| Tests fail or will not run | [Testing](/guide/testing) — check you used the right alias | -| Ads not rendering | `?ts_console=1` via [GPT Diagnostics](/guide/integrations/gpt-diagnostics); then the ad-stack gate | -| Behaviour differs on a real site | [Dev Proxy](/guide/ts-dev-proxy) | -| Config rejected on push | [Configuration](/guide/configuration) and [CLI](/guide/cli) | - -Consent is worth calling out: if the consent gate closes, there are no bids and -no ads, and the symptom looks like a broken auction rather than a consent -decision. Check consent state before debugging demand. +| Symptom | Look at | +| -------------------------------- | ----------------------------------------------------------------------------------------- | +| Build or config error | [Error Reference](/guide/error-reference) | +| Tests fail or will not run | [Testing](/guide/testing) — check you used the right alias | +| Ads not rendering | `?ts_console=1` via [GPT Diagnostics](/guide/integrations/gpt-diagnostics), then the gate | +| Behaviour differs on a real site | [Dev Proxy](/guide/ts-dev-proxy) | +| Config rejected on push | [Configuration](/guide/configuration) and [CLI](/guide/cli) | ## Contributing From 89f26b848f3fce7ad534371f3c72f228adf0a44a Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Mon, 14 Sep 2026 13:47:58 -0700 Subject: [PATCH 3/8] Explain how to view the documentation site locally A new engineer has no in-site instructions for running the docs. The VitePress workflow lives in docs/README.md, which srcExclude keeps out of the published site, so it is invisible to anyone browsing the guides. Add a section covering npm ci and npm run dev on port 5173, how a page maps to a Markdown file, and the format/lint/build checks to run before opening a pull request, calling out that the build fails on dead internal links. Note that tool versions come from .tool-versions through the config substitution rather than being typed literally. --- docs/guide/onboarding-new-engineer.md | 40 +++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/docs/guide/onboarding-new-engineer.md b/docs/guide/onboarding-new-engineer.md index 933700f2c..9db7c8b90 100644 --- a/docs/guide/onboarding-new-engineer.md +++ b/docs/guide/onboarding-new-engineer.md @@ -206,6 +206,46 @@ local build, which is the only practical way to reproduce most production issues. [GPT Diagnostics](/guide/integrations/gpt-diagnostics) explains `?ts_console=1`, the first thing to reach for when ads do not render. +### Reading these docs locally + +This documentation is a [VitePress](https://vitepress.dev) site that lives in +`docs/`. You are probably reading the published version, but running it locally +gives you full-text search over every guide and lets you preview any change you +make before opening a pull request. + +```bash +cd docs +npm ci +npm run dev +``` + +The site is served at `http://localhost:5173`. Pages reload as you save, so you +can keep it open while you read. Stop it with `Ctrl+C`. + +Each page maps to one Markdown file under `docs/guide/`, so the fastest way to +find the source of something you are reading is to search the repository for a +phrase from the page. + +If you edit a page, run these before opening a pull request: + +```bash +npm run format:write # apply Prettier formatting +npm run lint # ESLint +npm run build # production build; fails on broken internal links +``` + +`npm run build` is the one that matters most: it fails the build on a dead +internal link, so it catches a mistyped `/guide/...` path that would otherwise +ship. `npm run preview` serves the built output if you want to check the +production result. + +Version numbers such as the Rust and Viceroy versions on this page are not +written literally in the Markdown. Each entry in `.tool-versions` gets a +double-brace placeholder named after the tool in upper case followed by +`_VERSION`, and `docs/.vitepress/config.mts` substitutes it at build time. So +tool versions in the docs cannot drift from the pinned toolchain. If you need +to cite one in a page, use the placeholder rather than typing the number. + ### Local origin stub To exercise the first-party proxy against a fully local origin, point the From ca4ebd101f65d0670b2a4b9d77c6e8b43df164b9 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Mon, 14 Sep 2026 13:48:57 -0700 Subject: [PATCH 4/8] Correct the local documentation URL to include the base path The dev server instructions pointed at http://localhost:5173, which redirects instead of serving the home page. The site sets base to /trusted-server for GitHub Pages, so the working URL is http://localhost:5173/trusted-server/. Verified against a running dev server: the bare root returns 302 and the based path returns 200. --- docs/guide/onboarding-new-engineer.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/guide/onboarding-new-engineer.md b/docs/guide/onboarding-new-engineer.md index 9db7c8b90..7cbdb9b5d 100644 --- a/docs/guide/onboarding-new-engineer.md +++ b/docs/guide/onboarding-new-engineer.md @@ -219,8 +219,11 @@ npm ci npm run dev ``` -The site is served at `http://localhost:5173`. Pages reload as you save, so you -can keep it open while you read. Stop it with `Ctrl+C`. +The site is served at `http://localhost:5173/trusted-server/`. The +`/trusted-server/` suffix matters: the site sets a `base` path for GitHub +Pages, so the bare `http://localhost:5173` redirects rather than serving the +home page. VitePress prints the correct URL when it starts. Pages reload as you +save, so you can keep it open while you read. Stop it with `Ctrl+C`. Each page maps to one Markdown file under `docs/guide/`, so the fastest way to find the source of something you are reading is to search the repository for a From c9abf12923468646c04f4f7e1c6b3f132c698fb2 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Mon, 14 Sep 2026 13:53:55 -0700 Subject: [PATCH 5/8] Convert the architecture diagram to Mermaid The ASCII box drawing is replaced with a Mermaid flowchart, matching the style already used in architecture.md, edge-cookies.md, and first-party-proxy.md. The site already configures withMermaid, so no tooling change is needed. Node labels are single words. Multi-line labels were tried first and clipped: Mermaid sizes the box to the explicit break count but not to its own text wrapping, so the last line of each middle node was cut off at the border. The component detail moved to a table under the diagram, which is easier to scan than cramped box text anyway. Verified in a browser against the dev server rather than the static build, since the mermaid div is empty until client-side hydration: 8 nodes render with SVG, every label legible, and the diagram adapts correctly in both light and dark themes. --- docs/guide/onboarding-new-engineer.md | 62 ++++++++++++++------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/docs/guide/onboarding-new-engineer.md b/docs/guide/onboarding-new-engineer.md index 7cbdb9b5d..5c8b0d24c 100644 --- a/docs/guide/onboarding-new-engineer.md +++ b/docs/guide/onboarding-new-engineer.md @@ -38,36 +38,40 @@ into WebAssembly running on edge platforms. ## Architecture at a glance +```mermaid +flowchart TD + browser["Browser"] + origin["Publisher origin"] + demand["Demand partners"] + + subgraph edge["Edge runtime"] + direction TB + adapter["Adapter"] + proxy["Proxy"] + publisher["Publisher"] + integrations["Integrations"] + storage["Storage"] + + adapter --> proxy + adapter --> publisher + adapter --> integrations + proxy --> storage + publisher --> storage + integrations --> storage + end + + browser --> adapter + publisher --> origin + publisher --> demand ``` - User's browser - │ - ▼ -┌──────────────────────────────────────────────────────────┐ -│ Edge runtime (adapter + core) │ -│ │ -│ Adapter (Fastly / Cloudflare / Spin / Axum) │ -│ • Entry point, routing, platform bindings │ -│ • Client IP, TLS signals, EC request state │ -│ │ │ -│ ┌────────────────┼────────────────┐ │ -│ ▼ ▼ ▼ │ -│ ┌───────────┐ ┌──────────────┐ ┌──────────────┐ │ -│ │ Proxy │ │ Publisher │ │ Integrations │ │ -│ │ │ │ │ │ │ │ -│ │ /first- │ │ Origin fetch │ │ Prebid, GPT, │ │ -│ │ party/* │ │ Ad-stack gate│ │ APS, consent │ │ -│ │ Creative │ │ Auction │ │ vendors, ... │ │ -│ │ rewriting │ │ HTML rewrite │ │ │ │ -│ └───────────┘ └──────────────┘ └──────────────┘ │ -│ │ -│ Storage layer │ -│ • KV stores • Config stores • Secret stores │ -└──────────────────────────────────────────────────────────┘ - │ - ┌───────────────┴───────────────┐ - ▼ ▼ - Publisher origin Demand partners -``` + +| Component | Responsibility | +| ---------------- | -------------------------------------------------------------------------------------------------------------------------- | +| **Adapter** | Entry point for Fastly, Cloudflare, Spin, or Axum: routing, platform bindings, client IP and TLS signals, EC request state | +| **Proxy** | `/first-party/*` endpoints and creative rewriting | +| **Publisher** | Origin fetch, ad-stack gate, auction dispatch, HTML rewriting | +| **Integrations** | Prebid, GPT, APS, and consent vendors | +| **Storage** | KV, config, and secret stores | The core crate is runtime-agnostic; anything platform-specific lives in an adapter. A test enforces that core never imports the Fastly SDK. From 84f9e9ee48fee77708f73cd7d64f38ad0476c705 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Mon, 14 Sep 2026 13:58:52 -0700 Subject: [PATCH 6/8] Narrow the page to setup and defer to the restored onboarding guide Rebased onto 5c043a324, which restores docs/guide/onboarding.md as a published, sidebar-linked page. That page now covers the mental model, request trace, vocabulary, code map, and triage map, so most of this page duplicated it. Its request trace is also more accurate than the one here was: it names dispatch_fallback and the EdgeZero router, where this page had promoted the run_pre_route_filters helper to a top-level step. Keep only what the onboarding guide does not cover: - how to run and edit the VitePress site, including the base-path URL, the pre-pull-request checks, and the version-placeholder and Mermaid conventions - the local origin stub that proves the first-party proxy end to end - a build-traps table for environment failures whose error messages do not name their own cause Retitle to "New Engineer Setup" so the sidebar distinguishes it from Onboarding, and link out to that page rather than restating it. --- docs/guide/onboarding-new-engineer.md | 318 +++++--------------------- 1 file changed, 57 insertions(+), 261 deletions(-) diff --git a/docs/guide/onboarding-new-engineer.md b/docs/guide/onboarding-new-engineer.md index 5c8b0d24c..31a27d160 100644 --- a/docs/guide/onboarding-new-engineer.md +++ b/docs/guide/onboarding-new-engineer.md @@ -1,221 +1,20 @@ -# New Engineer Onboarding +# New Engineer Setup -This page orients a new engineer on the Trusted Server codebase. It answers the -questions the reference guides assume you already know: what problem the system -solves, how one request flows through it, what the ad-tech vocabulary means -here, and what to do in your first week. +Practical setup notes for your first days in the repository: how to read and +edit this documentation site, how to prove the first-party proxy works end to +end locally, and the build traps that cost new engineers the most time. -Read [What is Trusted Server?](/guide/what-is-trusted-server) for the product -framing and [Getting Started](/guide/getting-started) for setup. This page is -the bridge between them and the rest of the documentation. +Read [Onboarding](/guide/onboarding) first. It covers the mental model, a trace +of a publisher request, the vocabulary used here, the code map, and a triage +map. [Getting Started](/guide/getting-started) covers prerequisites and running +an adapter. This page is the practical companion to both and does not repeat +them. -## Project overview +## Reading these docs locally -Trusted Server is an open-source edge computing framework from IAB Tech Lab -that moves advertising operations out of third-party browser JavaScript and -into WebAssembly running on edge platforms. - -### The problem it solves - -- **Privacy restrictions.** Third-party cookie deprecation and tracking - prevention limit traditional advertising. -- **Third-party dependency.** Publishers have little control over the - third-party scripts running on their pages. -- **Performance.** Multiple third-party scripts slow page load. -- **Data control.** Publishers need control over how and with whom data is - shared. - -### What edge position buys - -- **First-party context.** Ads and assets are served from the publisher's own - domain. -- **Consent enforcement.** Consent is read and enforced before any demand call - is made. -- **Better performance.** Server-side processing reduces client-side - JavaScript. -- **Identity at the edge.** The Edge Cookie (EC) ID is derived server-side - rather than written by page JavaScript. - -## Architecture at a glance - -```mermaid -flowchart TD - browser["Browser"] - origin["Publisher origin"] - demand["Demand partners"] - - subgraph edge["Edge runtime"] - direction TB - adapter["Adapter"] - proxy["Proxy"] - publisher["Publisher"] - integrations["Integrations"] - storage["Storage"] - - adapter --> proxy - adapter --> publisher - adapter --> integrations - proxy --> storage - publisher --> storage - integrations --> storage - end - - browser --> adapter - publisher --> origin - publisher --> demand -``` - -| Component | Responsibility | -| ---------------- | -------------------------------------------------------------------------------------------------------------------------- | -| **Adapter** | Entry point for Fastly, Cloudflare, Spin, or Axum: routing, platform bindings, client IP and TLS signals, EC request state | -| **Proxy** | `/first-party/*` endpoints and creative rewriting | -| **Publisher** | Origin fetch, ad-stack gate, auction dispatch, HTML rewriting | -| **Integrations** | Prebid, GPT, APS, and consent vendors | -| **Storage** | KV, config, and secret stores | - -The core crate is runtime-agnostic; anything platform-specific lives in an -adapter. A test enforces that core never imports the Fastly SDK. - -### Technology stack - -| Layer | Technology | -| -------------- | ---------------------------------------- | -| Language | Rust {{RUST_VERSION}} | -| Runtime | WebAssembly (`wasm32-wasip1` for Fastly) | -| Edge platforms | Fastly Compute, Cloudflare Workers, Spin | -| Dev server | Axum (native) | -| Client library | TypeScript (tsjs) | -| Build tools | Cargo, esbuild | - -## The request path - -This is the single most useful thing to understand. A publisher page request -travels roughly this route. Line numbers drift; use the function names. - -| Step | Where | What happens | -| ---- | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | -| 1 | `adapter-fastly/src/main.rs` — `main` | `/health` short-circuits before anything else loads, then `edgezero_main` runs | -| 2 | `main.rs` — `edgezero_main` | Client IP is resolved and sanitized; trusted TLS headers are re-injected; client and device signals are captured | -| 3 | `adapter-fastly/src/app.rs` — `build_ec_request_state` | Device signals, bot gate, geo, and the EC context are assembled | -| 4 | `app.rs` — `run_pre_route_filters` | Integration request filters run before routing | -| 5 | `core/src/publisher.rs` — `handle_publisher_request` | The main publisher path | -| 6 | `core/src/creative_opportunities.rs` — `evaluate_ad_stack_gate` | Decides whether the ad stack runs at all for this request | -| 7 | `publisher.rs` — auction dispatch | Bids are requested **before** the origin response is sent, while the original client headers are intact | -| 8 | `core/src/html_processor.rs` — `create_html_processor` | One streaming pass rewrites URLs and injects integration scripts | -| 9 | `publisher.rs` | The response is finalized, EC state is written, and the body is streamed | - -Two details in that list surprise people: - -- **The auction starts before the origin responds.** It is not triggered by the - browser. Step 7 happens while step 8 is still waiting on the publisher. -- **The ad-stack gate can turn everything off.** If step 6 declines, no auction - runs and no scripts are injected. When ads are missing, check the gate before - suspecting the auction. - -See [Architecture](/guide/architecture) for the component view and -[Auction Orchestration](/guide/auction-orchestration) for the auction itself. - -## Key concepts - -### First-party proxying - -Instead of loading ad creatives and vendor scripts directly from third-party -domains, Trusted Server proxies them through first-party endpoints: - -``` -Before: Browser → ad-server.example/creative.html -After: Browser → publisher.example/first-party/proxy?tsurl=... -``` - -Everything stays under the publisher's domain, which avoids third-party cookie -restrictions and tracking prevention. Proxy URLs are signed, so the endpoint -cannot be used as an open relay. See -[First-Party Proxy](/guide/first-party-proxy). - -### Edge Cookie identity - -The EC ID is a privacy-preserving identifier derived at the edge with -HMAC-SHA256 rather than written by page JavaScript. It is deterministic for the -same inputs, non-reversible, and publisher-controlled. Rotating the passphrase -resets the identity graph. See [Edge Cookies](/guide/edge-cookies). - -### Integration modules - -Each vendor lives in its own module under -`crates/trusted-server-core/src/integrations/` and registers the hooks it -needs — proxying, request filtering, attribute rewriting, script rewriting, -HTML post-processing, or head injection. Browser-side counterparts live in -`crates/trusted-server-js/lib/src/integrations/`. See the -[Integration Guide](/guide/integration-guide) before adding one. - -### Request signing - -Ed25519 signing authenticates outbound API requests. Public keys are published -at `/.well-known/trusted-server.json`, and rotation is supported with a grace -period. See [Request Signing](/guide/request-signing) and -[Key Rotation](/guide/key-rotation). - -## Where the code lives - -| Path | What it is | -| ---------------------------------------------- | ----------------------------------------------------------------------- | -| `crates/trusted-server-core/` | Nearly all the logic. Start here. | -| `crates/trusted-server-core/src/publisher.rs` | The main request path. Large — navigate by function, not top to bottom. | -| `crates/trusted-server-core/src/auction/` | Auction orchestration, providers, and bidders. | -| `crates/trusted-server-core/src/ec/` | Edge Cookie identity. | -| `crates/trusted-server-core/src/consent/` | Consent parsing and enforcement. | -| `crates/trusted-server-core/src/integrations/` | One module per vendor integration. | -| `crates/trusted-server-js/lib/src/` | The browser-side TypeScript that ships to the page. | -| `crates/trusted-server-adapter-*/` | Per-runtime entry points. The Fastly adapter is production. | -| `crates/trusted-server-cli/` | The `ts` operator CLI. | - -## Vocabulary - -The reference guides use these terms without defining them. Here is what each -one means _in this codebase_. - -| Term | Meaning here | -| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | -| **Impression** | One opportunity to show one ad in one slot on one page view. | -| **Ad slot** / **placement** | A region of the page that can hold an ad. Identified by a DOM element ID. | -| **Creative** | The actual ad markup that gets rendered — HTML, an image, or a script. Arrives from a bidder and renders inside a sandboxed iframe. | -| **Bid** | An offer to buy one impression at a price. | -| **Auction** | Collecting bids for the page's slots and choosing winners. | -| **Header bidding** | Running an auction among several demand sources before calling the ad server, so they compete rather than being asked in a fixed order. | -| **SSP** | Supply-side platform — sells the publisher's inventory. A demand source from our perspective. | -| **DSP** | Demand-side platform — buys on behalf of advertisers, usually via an SSP. | -| **CPM** | Cost per thousand impressions, the usual unit of a bid price. | -| **OpenRTB** | The IAB-standard JSON format for bid requests and responses. Our types live in the `trusted-server-openrtb` crate. | -| **Prebid** | The open-source header-bidding framework. We ship a browser shim plus an optional first-party bundle. | -| **GPT** | Google Publisher Tag, the browser library that requests ads from Google Ad Manager. | -| **GAM** | Google Ad Manager, the ad server that decides what finally renders. | -| **EC ID** | Edge Cookie ID. See [Edge Cookies](/guide/edge-cookies). | -| **Consent string** | An encoded record of what a user agreed to. TCF covers GDPR, GPP is the newer multi-jurisdiction container, GPC is a browser-level opt-out signal. | -| **CMP** | Consent management platform — the vendor that shows the consent banner and produces the consent string. | - -## Your first week - -**Get it running.** Follow [Getting Started](/guide/getting-started). The Axum -dev server is the fastest path and needs no cloud account. - -**Read one request end to end.** Follow the request-path table above through the -source with the files open. That single exercise explains more than any other. - -**Make a small change.** Pick something with a test beside it, change it, and -watch the test fail. `cargo test-axum` is the quickest loop. - -**Learn the tooling you will need later.** -[Dev Proxy](/guide/ts-dev-proxy) serves a publisher's real hostname from your -local build, which is the only practical way to reproduce most production -issues. [GPT Diagnostics](/guide/integrations/gpt-diagnostics) explains -`?ts_console=1`, the first thing to reach for when ads do not render. - -### Reading these docs locally - -This documentation is a [VitePress](https://vitepress.dev) site that lives in -`docs/`. You are probably reading the published version, but running it locally -gives you full-text search over every guide and lets you preview any change you -make before opening a pull request. +This documentation is a [VitePress](https://vitepress.dev) site in `docs/`. +Running it locally gives you full-text search across every guide and lets you +preview a change before opening a pull request. ```bash cd docs @@ -227,13 +26,13 @@ The site is served at `http://localhost:5173/trusted-server/`. The `/trusted-server/` suffix matters: the site sets a `base` path for GitHub Pages, so the bare `http://localhost:5173` redirects rather than serving the home page. VitePress prints the correct URL when it starts. Pages reload as you -save, so you can keep it open while you read. Stop it with `Ctrl+C`. +save. Stop it with `Ctrl+C`. -Each page maps to one Markdown file under `docs/guide/`, so the fastest way to -find the source of something you are reading is to search the repository for a +Each page is one Markdown file under `docs/guide/`, so the fastest way to find +the source of something you are reading is to search the repository for a phrase from the page. -If you edit a page, run these before opening a pull request: +### Before you open a documentation pull request ```bash npm run format:write # apply Prettier formatting @@ -241,22 +40,28 @@ npm run lint # ESLint npm run build # production build; fails on broken internal links ``` -`npm run build` is the one that matters most: it fails the build on a dead -internal link, so it catches a mistyped `/guide/...` path that would otherwise -ship. `npm run preview` serves the built output if you want to check the -production result. +`npm run build` is the one that matters most: it fails on a dead internal link, +so it catches a mistyped `/guide/...` path that would otherwise ship. +`npm run preview` serves the built output if you want to check the production +result. + +Two conventions worth knowing before you edit a page: -Version numbers such as the Rust and Viceroy versions on this page are not -written literally in the Markdown. Each entry in `.tool-versions` gets a -double-brace placeholder named after the tool in upper case followed by -`_VERSION`, and `docs/.vitepress/config.mts` substitutes it at build time. So -tool versions in the docs cannot drift from the pinned toolchain. If you need -to cite one in a page, use the placeholder rather than typing the number. +- **Tool versions are not written literally.** Each entry in `.tool-versions` + gets a double-brace placeholder named after the tool in upper case followed + by `_VERSION`, and `docs/.vitepress/config.mts` substitutes it at build time. + Use the placeholder rather than typing a version number, so the docs cannot + drift from the pinned toolchain. +- **Diagrams are Mermaid.** Use a ` ```mermaid ` block; the site already + configures the plugin. Keep node labels short. Mermaid sizes a node box to + its explicit line breaks but not to its own text wrapping, so a long label + renders clipped at the box border. Put the detail in a table beside the + diagram instead. -### Local origin stub +## Prove the first-party proxy locally -To exercise the first-party proxy against a fully local origin, point the -publisher origin at a local server and sign an asset URL: +A useful early exercise: run a local origin and fetch a file through the +first-party proxy, which exercises signing and proxying end to end. ```bash # Terminal 1: serve a file from a local origin @@ -267,37 +72,28 @@ python3 -m http.server 9090 --directory /tmp/ts-origin ``` ```bash -# Terminal 2: with the server running, sign the URL and fetch it +# Terminal 2: with the server running, sign the URL curl -s "http://127.0.0.1:7676/first-party/sign?url=http://localhost:9090/hello.txt" ``` Request the signed path that comes back; the response body should be -`hello from origin`. - -## Common issues - -| Issue | Solution | -| ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | -| Bare `cargo build` / `cargo test` fails | The adapters target different architectures. Use the aliases in `.cargo/config.toml`, such as `cargo test-axum` or `cargo test-fastly`. | -| `ts config validate` rejects a valid config | `ts` is built from this repository. Reinstall it with `cargo install-cli` after pulling changes to configuration types. | -| Rust build fails in the JS step | The Rust build runs the JavaScript build, so Node from `.tool-versions` is required even for Rust-only work. | -| `cargo test-fastly` fails on Viceroy | Install the pinned version: `cargo install viceroy --version {{VICEROY_VERSION}} --locked --force`. | -| Tests pass locally but fail in CI | Run `cargo fmt --all -- --check` and the target-matched clippy aliases; CI denies warnings. | -| No bids and no ads | Check consent state and the ad-stack gate before suspecting demand. | - -## When something does not work - -| Symptom | Look at | -| -------------------------------- | ----------------------------------------------------------------------------------------- | -| Build or config error | [Error Reference](/guide/error-reference) | -| Tests fail or will not run | [Testing](/guide/testing) — check you used the right alias | -| Ads not rendering | `?ts_console=1` via [GPT Diagnostics](/guide/integrations/gpt-diagnostics), then the gate | -| Behaviour differs on a real site | [Dev Proxy](/guide/ts-dev-proxy) | -| Config rejected on push | [Configuration](/guide/configuration) and [CLI](/guide/cli) | - -## Contributing - -Read [CONTRIBUTING.md](https://github.com/IABTechLab/trusted-server/blob/main/CONTRIBUTING.md) -for the pull-request process and commit-message conventions, and `CLAUDE.md` -for coding standards and the full CI gate list. Run the gates that match what -you changed before opening a pull request. +`hello from origin`. If signing fails, the proxy secret is usually missing from +the environment — see [Getting Started](/guide/getting-started). + +## Build traps + +These are the failures most likely to cost you an afternoon. They are +environment problems, not code problems, and none of them produce an error +message that names its own cause. + +| What you see | What is actually wrong | +| -------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Bare `cargo test` fails while linking, with missing `fastly` symbols | The workspace mixes native and WebAssembly targets. Use the target-specific aliases in `.cargo/config.toml`, such as `cargo test-axum`. Note that bare `cargo check` succeeds, so it gives a false green. | +| `ts config validate` rejects a configuration file that looks correct | The installed `ts` predates a configuration-schema change. Re-run `cargo install-cli` after pulling. | +| A Rust build fails inside a JavaScript step | The Rust build generates the browser bundles, so the pinned Node from `.tool-versions` is required even for Rust-only work. `dist/` is not checked in. | +| `ts` cannot find a manifest | `edgezero.toml` uses repository-relative paths; run `ts` from the repository root. | +| `cargo test-fastly` fails on the runtime | Install the pinned simulator: `cargo install viceroy --version {{VICEROY_VERSION}} --locked --force`. | +| Tests pass locally but CI fails | Run `cargo fmt --all -- --check` and the target-matched clippy aliases. CI denies warnings. | + +For the full gate list, see `CLAUDE.md`. For which gate matches your change, +see [Testing](/guide/testing). From 3e001965800b5d4dc02abb730a2f18670019b760 Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Mon, 14 Sep 2026 13:59:58 -0700 Subject: [PATCH 7/8] Add the day-one access requests A new engineer needs a Google account for calendar and shared documents, a GitHub account with write access to the repository, access to the project board to find work, and a Fastly account with an API token to deploy or inspect a service. These are specific to a new hire rather than to working in the codebase, so they belong here and not in the general onboarding guide. The section also notes that none of them block getting started: the Axum adapter runs locally with no edge account. The repository and board URLs are carried over from the onboarding guide added in #183. --- docs/guide/onboarding-new-engineer.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/guide/onboarding-new-engineer.md b/docs/guide/onboarding-new-engineer.md index 31a27d160..087e82b75 100644 --- a/docs/guide/onboarding-new-engineer.md +++ b/docs/guide/onboarding-new-engineer.md @@ -10,6 +10,23 @@ map. [Getting Started](/guide/getting-started) covers prerequisites and running an adapter. This page is the practical companion to both and does not repeat them. +## Request access + +Ask your manager or onboarding contact for these on your first day. Some take +time to be granted, so request them before you need them. Nothing else on this +page requires them, so you can start reading and building while you wait. + +| Access | What it is for | Where | +| ------------------------ | ------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | +| **Google account** | Calendar invitations for team meetings, and shared documents | Ask your manager or onboarding contact | +| **GitHub account** | Read and write access to the repository, so you can push branches and open pull requests | [IABTechLab/trusted-server](https://github.com/IABTechLab/trusted-server) | +| **GitHub project board** | The team's planned and in-flight work, and where you find a first task | [Trusted Server project board](https://github.com/orgs/IABTechLab/projects/3) | +| **Fastly account** | The production edge platform. You need an account and an API token to deploy or inspect a service | [manage.fastly.com](https://manage.fastly.com) | + +A Fastly account is only needed for deploying and inspecting real services. +Local development on the Axum adapter needs no edge account at all, so it is +the right place to start on day one. + ## Reading these docs locally This documentation is a [VitePress](https://vitepress.dev) site in `docs/`. From f23cc4ef6a8876c6b9b200406704674ceb3aef6e Mon Sep 17 00:00:00 2001 From: Aram Grigoryan <132480+aram356@users.noreply.github.com> Date: Mon, 14 Sep 2026 23:40:20 -0700 Subject: [PATCH 8/8] Add a first-issue section to the new-engineer setup page Point a new engineer at three issues scoped for a first contribution: #1093 (root Markdown escapes the Prettier gate), #1063 (a bare `ts dev proxy` prints an internal error report instead of help), and #1144 (partner token placeholders are not rejected at startup). Each is small, self-contained, and has a neighboring test to copy. The section records the suggested order and the two prerequisites that are not visible from the issues themselves: `ts dev proxy` is macOS-only, and the #1093 fix touches the high-traffic agent guide. --- docs/guide/onboarding-new-engineer.md | 31 +++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docs/guide/onboarding-new-engineer.md b/docs/guide/onboarding-new-engineer.md index 087e82b75..b23dbde5a 100644 --- a/docs/guide/onboarding-new-engineer.md +++ b/docs/guide/onboarding-new-engineer.md @@ -97,6 +97,37 @@ Request the signed path that comes back; the response body should be `hello from origin`. If signing fails, the proxy secret is usually missing from the environment — see [Getting Started](/guide/getting-started). +## Pick a first issue + +These three are scoped deliberately for a first contribution: each is small, +self-contained, has an existing test nearby to copy, and touches code that only +one caller depends on. Each issue carries its own reproduction, acceptance +criteria, and file-and-line pointers, so start by reading the issue in full. + +| Issue | What it is | Why it suits a first contribution | +| ----------------------------------------------------------------- | ------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | +| [#1093](https://github.com/IABTechLab/trusted-server/issues/1093) | Root Markdown escapes the Prettier gate, so several root pages fail `--check` | Documentation and CI only, no runtime risk. A good way to learn the workflow and the format gates before changing behavior. | +| [#1063](https://github.com/IABTechLab/trusted-server/issues/1063) | A bare `ts dev proxy` prints an internal error report instead of help | One crate, one argument struct, an existing neighboring test. Teaches Clap and this repository's `error-stack` conventions. | +| [#1144](https://github.com/IABTechLab/trusted-server/issues/1144) | Partner token placeholders from the config template are not rejected at startup | A single validation function with one production caller, and an adjacent branch to mirror. Teaches configuration validation. | + +Take them in that order if you want the gentlest ramp: #1093 exercises the +review and merge mechanics with nothing at stake, then #1063 and #1144 are real +behavior changes of a similar small size. + +Two notes before you start: + +- **#1063 is macOS-only.** `ts dev proxy` has its dependencies scoped to macOS, + so the subcommand does not exist on other hosts. It also needs an explicit + host target, because the workspace default target is WebAssembly. Pick a + different issue if you are not on a Mac. +- **#1093 edits `AGENTS.md`**, which is high-traffic. Check whether a large + documentation pull request is open before you start, and rebase rather than + forcing a conflict. + +If all three are taken, the project board is the place to look next. Ask in the +team channel before starting anything unlabelled, since an issue that reads as +small often is not. + ## Build traps These are the failures most likely to cost you an afternoon. They are