diff --git a/docs/js-asset-proxy-prd.md b/docs/js-asset-proxy-prd.md
new file mode 100644
index 00000000..c5a01d54
--- /dev/null
+++ b/docs/js-asset-proxy-prd.md
@@ -0,0 +1,356 @@
+# Product Requirements: First-Party JS Asset Proxy
+
+**Status:** Draft
+**Author:** Trusted Server Product
+**Last updated:** 2026-03-26
+
+---
+
+## Table of Contents
+
+1. [Overview](#1-overview)
+2. [Problem Statement](#2-problem-statement)
+3. [Goals and Non-Goals](#3-goals-and-non-goals)
+4. [Target Customers](#4-target-customers)
+5. [Opaque URL Routing](#5-opaque-url-routing)
+6. [KV Asset Cache](#6-kv-asset-cache)
+7. [HTML Script Tag Injection](#7-html-script-tag-injection)
+8. [Cache Lifecycle](#8-cache-lifecycle)
+9. [Configuration](#9-configuration)
+10. [Routes](#10-routes)
+11. [Security](#11-security)
+12. [Open Questions](#12-open-questions)
+13. [Success Metrics](#13-success-metrics)
+
+---
+
+## 1. Overview
+
+The JS Asset Proxy allows Trusted Server to fetch third-party JavaScript files from configured origin URLs, cache them in Fastly or similar KV Store under opaque first-party paths, and serve them from the publisher's own domain. When running in full HTML proxy mode, TS also injects the corresponding `
+
+
+```
+
+### 7.2 TS Lite mode (HTML proxy disabled)
+
+TS does not modify HTML responses. The publisher hardcodes the opaque `
+```
+
+### 7.3 Tag attributes
+
+The injected or hardcoded tag has no `async` or `defer` attributes by default. Ad tech bootstrapper scripts (Prebid loaders, vendor loaders) are render-blocking by design — they must execute before GPT slot definitions and auction calls. The ad tech partner controls whether their subsequently loaded scripts are async/defer.
+
+---
+
+## 8. Cache Lifecycle
+
+### 8.1 Initial TTL
+
+**1 hour (3600 seconds)** for v1. This matches the most common `Cache-Control: max-age=3600` from ad tech CDN origins. The TTL is configurable per asset entry in `trusted-server.toml` to accommodate partners with different cache policies.
+
+### 8.2 ETag-based conditional refresh
+
+On TTL expiry, TS issues a conditional `GET` with `If-None-Match: {stored_etag}` to the origin. A `304 Not Modified` response costs only a TCP roundtrip — no body is transferred. The KV body is reused as-is, and only the metadata `expires_at` is updated. This is the expected common case: vendor files are typically updated daily, not hourly.
+
+### 8.3 Cache warming
+
+On first request after deployment (cold KV), the origin fetch adds latency to that one request. This is acceptable for v1. A pre-warm step (fetching and populating KV at service deploy time) is a follow-on improvement.
+
+### 8.4 Manual invalidation
+
+TS operators can force a cache bust by deleting the KV entry via the KV management API. The next request will treat it as a cold miss and fetch from origin. No binary redeploy required.
+
+---
+
+## 9. Configuration
+
+New section in `trusted-server.toml`:
+
+```toml
+[kv_stores]
+js_asset_store = "golf-js-asset-store" # new, alongside ec_store and partner_store
+
+# One [[js_assets]] entry per proxied asset
+[[js_assets]]
+slug = "golf-WnLmpLyEjL:prebid-load"
+path = "/sdk/gWnLmpLy.js"
+origin_url = "https://web.prebidwrapper.com/golf-WnLmpLyEjL/default-v2/prebid-load.js"
+ttl_sec = 3600
+inject_in_head = true # inject