diff --git a/content/terraphim-llm-proxy/_index.md b/content/terraphim-llm-proxy/_index.md
index 9cea2a6..bb606e1 100644
--- a/content/terraphim-llm-proxy/_index.md
+++ b/content/terraphim-llm-proxy/_index.md
@@ -45,8 +45,32 @@ flagship quality — so the app pays for itself out of saved tokens.
Every listener is loopback-only; nothing leaves your machine except the
requests you send to your providers.
+
+
+
+
+
## Links
+- [Downloads](/terraphim-llm-proxy/download/)
- [Privacy policy](/terraphim-llm-proxy/privacy/)
- [Refund policy](/terraphim-llm-proxy/refunds/)
- [Support — GitHub issues](https://github.com/terraphim/terraphim-ai/issues)
diff --git a/content/terraphim-llm-proxy/download.md b/content/terraphim-llm-proxy/download.md
new file mode 100644
index 0000000..289ea21
--- /dev/null
+++ b/content/terraphim-llm-proxy/download.md
@@ -0,0 +1,49 @@
++++
+title = "Terraphim Proxy — Downloads"
+description = "Signed macOS releases of Terraphim Proxy for licensed customers."
+weight = 40
+template = "page.html"
+render = true
++++
+
+Signed macOS builds of Terraphim Proxy, served from the Terraphim Skills
+marketplace. Downloads are tied to the account you purchased with — sign
+in below with that account, then pick a release. Every artefact is listed
+with its SHA-256 so you can verify the download before opening it.
+
+
+
+
+ Purchase confirmed — your downloads are below.
+
+
+
+
Loading releases…
+
+
+
+
+
diff --git a/content/terraphim-llm-proxy/privacy.md b/content/terraphim-llm-proxy/privacy.md
index 5962bdb..e320576 100644
--- a/content/terraphim-llm-proxy/privacy.md
+++ b/content/terraphim-llm-proxy/privacy.md
@@ -1,7 +1,7 @@
+++
title = "Terraphim Proxy — Privacy Policy"
description = "Privacy policy for the Terraphim Proxy desktop application (cloud.terraphim.desktop)"
-date = 2026-10-22
+date = 2026-10-24
weight = 10
[taxonomies]
@@ -11,16 +11,21 @@ categories = ["Legal"]
toc = false
+++
-**Effective date: 22 October 2026.** Applies to the Terraphim Proxy macOS
+**Effective date: 24 October 2026.** Applies to the Terraphim Proxy macOS
application (`cloud.terraphim.desktop`), distributed via the Mac App
Store and direct download.
## The short version
-Terraphim Proxy collects **no data whatsoever**. There is no account system,
-no telemetry, no analytics, no crash reporting, and no third-party
-tracking SDK. Your credentials never leave your Mac except inside the
-requests you deliberately send to the LLM providers you configured.
+The Terraphim Proxy **application** collects **no data whatsoever**. There
+is no account system, no telemetry, no analytics, no crash reporting, and
+no third-party tracking SDK in the app. Your credentials never leave your
+Mac except inside the requests you deliberately send to the LLM providers
+you configured.
+
+Buying a direct download does create a small server-side record — kept by
+the Terraphim Skills marketplace, not by the app — described under
+*Purchases* below.
## What stays on your device
@@ -47,10 +52,21 @@ OpenAI, xAI, or any other provider you configure).
## Purchases
-The app is a one-time purchase processed entirely by Apple's App Store.
-Apple — not Terraphim Proxy — receives and processes your payment
-information. The app contains no in-app purchases, no subscription
-checks and no licence servers.
+**Mac App Store.** The app is a one-time purchase processed entirely by
+Apple's App Store. Apple — not Terraphim Proxy — receives and processes
+your payment information. The app contains no in-app purchases, no
+subscription checks and no licence servers.
+
+**Direct download.** Direct-download purchases are processed by Stripe
+via the Terraphim Skills marketplace (`api.terraphim-skills.md`). Stripe
+— not Terraphim — receives and processes your payment information. The
+marketplace stores the email address on your account, the fact of the
+purchase, and which release downloads it has served to your account;
+those records are used solely for entitlement checks and refund
+adjudication, as described in
+the [Refund policy](/terraphim-llm-proxy/refunds/). The application
+itself still collects nothing — this record lives entirely on the
+marketplace side.
## Permissions the app requests
diff --git a/content/terraphim-llm-proxy/refunds.md b/content/terraphim-llm-proxy/refunds.md
index 15da1ec..49c6687 100644
--- a/content/terraphim-llm-proxy/refunds.md
+++ b/content/terraphim-llm-proxy/refunds.md
@@ -6,7 +6,7 @@ template = "page.html"
render = true
+++
-**Last updated:** [CONFIRM: set to the date direct-download sales open]
+**Effective upon launch of direct-download sales.**
This policy covers **direct downloads purchased on terraphim.ai**.
Purchases made through the Mac App Store are refunded by Apple under the
@@ -15,22 +15,23 @@ not Terraphim, decides those refunds.
## The short version
-**You can refund any time before you download a release. Once your
-account has downloaded a release, the sale is final — unless the proxy
-does not function.**
+**Within 14 days of purchase you can get a full refund provided no
+release has been downloaded. Once your account has downloaded a release,
+the sale is final — unless the proxy does not function.**
This is the same shape as Steam's rule for games: a refund is available
while the product is untouched, and the usage measurement is what makes
-the rule fair to enforce. Steam measures playtime; we measure the
-download event.
+the rule fair to enforce. Steam measures playtime; we measure the moment
+our service first serves the download.
## Why we do it this way
A signed application bundle cannot be returned once it is copied to your
Mac — there is no undo. The download is the honest line between
"bought but not taken" and "taken", and it is a line we can measure: the
-download service records which releases your account has downloaded, and
-that record is the only thing the refund rule consults.
+service records the first time it serves a release download request to
+your account, and that record is the only thing the refund rule
+consults.
We considered the alternative — a blanket 14-day window — and rejected
it, because it would let anyone buy, download every release, and refund.
@@ -46,6 +47,12 @@ else.
| **Any release downloaded**, proxy does not function | Full refund — see *When the proxy does not function* below |
| More than 14 days after purchase, no download | Not automatic. Ask anyway — see *Outside the rules* below. |
+*"Downloaded" is counted when our service begins delivering the release
+to your account — the request is recorded when delivery starts, not when
+the bytes finish. A failed or interrupted delivery does not count as a
+completed download for refund purposes (see *Downloads were broken*
+below), and where the record is ambiguous we treat it in your favour.*
+
Refunds go back to the original payment method through Stripe. Card
refunds usually appear within 5–10 business days, depending on your bank
— that part is outside our control.
@@ -79,7 +86,7 @@ Email **[engineering@terraphim.ai](mailto:engineering@terraphim.ai)**
with the email address on the account you purchased with. For a defect
claim, include the app version and macOS version.
-We aim to process refund requests within **[CONFIRM: 3 business days]**.
+We aim to process refund requests within **3 business days**.
## Outside the rules
@@ -96,9 +103,10 @@ is not closed. We will look seriously at:
## What we record
To make this policy enforceable we keep a server-side record, tied to
-your purchase account, of **which releases you downloaded and when**.
-That is the entire record — the app itself collects no telemetry and
-reports nothing back. Purchase and download records are covered in the
+your purchase account, of **when our service first serves a release
+download request to you** — the version, the asset and the time. That is
+the entire record — the app itself collects no telemetry and reports
+nothing back. Purchase and download records are covered in the
[Privacy policy](/terraphim-llm-proxy/privacy/).
## Your statutory rights
diff --git a/static/js/proxy-buy.js b/static/js/proxy-buy.js
new file mode 100644
index 0000000..ac7d73e
--- /dev/null
+++ b/static/js/proxy-buy.js
@@ -0,0 +1,81 @@
+/* Terraphim Proxy — buy button.
+ Wires #proxy-buy-btn on /terraphim-llm-proxy/ to the Terraphim Skills
+ marketplace checkout. Contract: GET /api/billing/checkout?product=…
+ with a Bearer JWT returns {"url": ""}.
+
+ Sign-in is a round trip: the link carries ?return_to=, the
+ OAuth callback lands back here with the JWT in the fragment
+ (#token=), which captureToken() below moves into localStorage and
+ strips from the address bar. Requires /js/proxy-helpers.js loaded first. */
+(function () {
+ 'use strict';
+ var H = window.ProxyHelpers;
+ var btn = document.getElementById('proxy-buy-btn');
+ var status = document.getElementById('proxy-buy-status');
+ if (!H || !btn || !status) { return; }
+
+ /* Move a JWT handed back in the URL fragment (or, forward-compat, the
+ query) into localStorage, then scrub it from the address bar and
+ history — a JWT must never linger in either. */
+ function captureToken() {
+ var tok = H.tokenFromLocation(window.location);
+ if (!tok) { return; }
+ try { localStorage.setItem('terraphim_token', tok); } catch (e) { return; }
+ try {
+ var q = new URLSearchParams(window.location.search);
+ q.delete('token');
+ var qs = q.toString();
+ var hash = H.stripTokenFromHash(window.location.hash);
+ var clean = window.location.origin + window.location.pathname +
+ (qs ? '?' + qs : '') + (hash ? '#' + hash : '');
+ history.replaceState(null, '', clean);
+ } catch (e) { /* replaceState unavailable — token is stored anyway */ }
+ }
+
+ function getToken() {
+ try { return localStorage.getItem('terraphim_token'); } catch (e) { return null; }
+ }
+
+ function signInHref() {
+ var back = window.location.origin + window.location.pathname + window.location.search;
+ return H.signInUrl(back);
+ }
+
+ function showSignIn(note) {
+ status.innerHTML = note +
+ ' Sign in with GitHub, then try again.';
+ }
+
+ captureToken();
+
+ btn.addEventListener('click', function () {
+ var token = getToken();
+ if (!token) {
+ showSignIn('Purchasing needs a Terraphim account.');
+ return;
+ }
+ btn.disabled = true;
+ status.textContent = 'Preparing checkout…';
+ fetch(H.API + '/api/billing/checkout?product=terraphim-proxy', {
+ headers: { 'Authorization': 'Bearer ' + token }
+ })
+ .then(function (res) {
+ if (res.status === 401) {
+ showSignIn('Your session has expired —');
+ btn.disabled = false;
+ return null;
+ }
+ if (!res.ok) { throw new Error('checkout ' + res.status); }
+ return res.json();
+ })
+ .then(function (data) {
+ if (!data) { return; }
+ if (data.url) { window.location.href = data.url; return; }
+ throw new Error('checkout returned no url');
+ })
+ .catch(function () {
+ status.textContent = 'Checkout is unavailable right now — please try again later.';
+ btn.disabled = false;
+ });
+ });
+})();
diff --git a/static/js/proxy-download.js b/static/js/proxy-download.js
new file mode 100644
index 0000000..66e1d4d
--- /dev/null
+++ b/static/js/proxy-download.js
@@ -0,0 +1,195 @@
+/* Terraphim Proxy — entitlement-gated release downloads.
+ Renders the public release catalogue on /terraphim-llm-proxy/download/
+ and gates artefact fetches behind a Bearer JWT; 402 means the account
+ has not purchased. Contract (api.terraphim-skills.md):
+ GET /api/products/terraphim-proxy/releases
+ -> [{version, asset_name, size_bytes, sha256, published_at}]
+ (a wrapped {"releases": [...]} shape is tolerated)
+ GET /api/products/terraphim-proxy/releases/{version}/{asset}
+ -> artefact bytes + X-SHA256 header; 402 {"error":"purchase_required"}.
+
+ Sign-in is a round trip: links carry ?return_to=, the OAuth
+ callback lands back here with the JWT in the fragment (#token=),
+ which captureToken() moves into localStorage and strips from the
+ address bar. Requires /js/proxy-helpers.js loaded first. */
+(function () {
+ 'use strict';
+ var H = window.ProxyHelpers;
+ var banner = document.getElementById('proxy-dl-banner');
+ var notice = document.getElementById('proxy-dl-notice');
+ var wrap = document.getElementById('proxy-dl-wrap');
+ if (!H || !notice || !wrap) { return; }
+
+ var BUY_URL = '/terraphim-llm-proxy/#your-keys-your-costs';
+
+ /* Move a JWT handed back in the URL fragment (or, forward-compat, the
+ query) into localStorage, then scrub it from the address bar and
+ history — a JWT must never linger in either. */
+ function captureToken() {
+ var tok = H.tokenFromLocation(window.location);
+ if (!tok) { return; }
+ try { localStorage.setItem('terraphim_token', tok); } catch (e) { return; }
+ try {
+ var q = new URLSearchParams(window.location.search);
+ q.delete('token');
+ var qs = q.toString();
+ var hash = H.stripTokenFromHash(window.location.hash);
+ var clean = window.location.origin + window.location.pathname +
+ (qs ? '?' + qs : '') + (hash ? '#' + hash : '');
+ history.replaceState(null, '', clean);
+ } catch (e) { /* replaceState unavailable — token is stored anyway */ }
+ }
+
+ function getToken() {
+ try { return localStorage.getItem('terraphim_token'); } catch (e) { return null; }
+ }
+
+ function signInHref() {
+ var back = window.location.origin + window.location.pathname + window.location.search;
+ return H.signInUrl(back);
+ }
+
+ captureToken();
+
+ /* ?checkout=success comes back from Stripe. Only claim the purchase is
+ confirmed when a token is present — anyone can forge the parameter,
+ so signed-out visitors get neutral wording instead. */
+ try {
+ if (new URLSearchParams(window.location.search).get('checkout') === 'success' && banner) {
+ if (!getToken()) {
+ banner.innerHTML =
+ ' ' +
+ 'Checkout completed — sign in below to confirm download access.';
+ }
+ banner.hidden = false;
+ }
+ } catch (e) { /* malformed query — ignore */ }
+
+ function signInPrompt() {
+ notice.innerHTML =
+ '