# Audio Vault Product Decisions

**Recorded:** September 7, 2026 (interactive interview, Dani Roxberry)  
**Follow-up:** September 7, 2026 — [chat2-decisions.md](chat2-decisions.md) (launch/freeze alignment, statutory refunds, CONFIG cleanup)  
**Status:** Authoritative for implementation until explicitly revised  
**Configuration:** Defaults live in [.env.example](../../.env.example); full reference in [CONFIG.md](CONFIG.md)

Later explicit user decisions control over research documents and assistant suggestions. Checkboxes in [TODO.md](TODO.md) track **evidence**, not these decisions themselves.

---

## Schedule, Acceptance, and Capacity

| ID | Decision | Notes |
|---|---|---|
| D-01 | Code freeze **September 10, 2026, 11:59 PM US Eastern** — **same calendar day as public launch** ([D-03](DECISIONS.md#schedule-acceptance-and-capacity)) | `CODE_FREEZE_DATE`, AV-R04 |
| D-02 | End-of-September milestone (no separate demo date) must include: signup/login, uploads (partial OK), browser playback, **simulated streamer**, **Stripe test checkout** | Calculator-only demo not required |
| D-03 | **Public launch September 10, 2026 (US Eastern)** — full product go-live; **same day as code freeze** ([D-01](DECISIONS.md#schedule-acceptance-and-capacity)) | `PUBLIC_LAUNCH_DATE`; revised from Q3-A TBD |
| D-04 | **Dani** is final product acceptance owner | Q4-A |
| D-05 | **Solo + Cursor agents** for implementation | Q5-A |
| D-06 | If schedule slips, cut **metadata auto-match** first (manual catalog OK) | Q5 scope cut #1 |
| D-132 | **Schedule acknowledgment (a3 A):** Dani **acknowledges** [D-01](DECISIONS.md#schedule-acceptance-and-capacity)–[D-06](DECISIONS.md#schedule-acceptance-and-capacity) as the active plan (freeze, milestone scope, capacity, metadata-first slip cut) | AV-T24; [VALIDATION.md](VALIDATION.md) |

---

## Infrastructure and Operations

| ID | Decision | Env / tasks |
|---|---|---|
| D-10 | Go API on **Linux VPS @ Servarica** | Deployment |
| D-11 | **PostgreSQL on same Servarica VPS** | `DATABASE_URL` |
| D-12 | Customer audio: **Cloudflare R2, US jurisdiction** | `S3_JURISDICTION=us` |
| D-13 | Observability: **logs + uptime + Sentry + Prometheus/Grafana** | Deployment |
| D-14 | Backups: **daily, 24h RPO, 4h RTO** | `BACKUP_RPO_HOURS`, `BACKUP_RTO_HOURS` |
| D-15 | Incidents: **Dani, business hours US Eastern** | `INCIDENT_*` |
| D-16 | Static site/PWA: **Cloudflare Pages on apex** | Retire GitHub Pages / `docs/CNAME` plan |
| D-17 | Go API separate from Pages; **no customer audio on apex** | Private R2 only |
| D-18 | DNS import: **before launch only** (after AV-T35 review) | AV-T34–38 |
| D-19 | **Email domain:** `audiovau.lt` on Apple iCloud custom domain (MX) for **inbound** mailboxes; **outbound transactional** via Resend ([D-131](DECISIONS.md#infrastructure-and-operations)) | AV-T43 |
| D-131 | **Outbound transactional email (E):** Magic links and password resets via **Resend** from `@audiovau.lt` (e.g. **`noreply@audiovau.lt`**). **Supersedes** prior iCloud-SMTP-only auth-mail decision | AV-T08, AV-T43; `AUTH_EMAIL_PROVIDER`, `RESEND_API_KEY` |
| D-143 | **Inbound mailboxes (E):** Apple iCloud receives **inbound-only** **`abuse@audiovau.lt`**, **`privacy@audiovau.lt`**, and **`support@audiovau.lt`** ([D-156](DECISIONS.md#infrastructure-and-operations)) ([D-101](DECISIONS.md#legal-policies-and-accessibility)). No export mailbox until public beta ([D-120](DECISIONS.md#legal-policies-and-accessibility)) | AV-T43, AV-T40–T42 |
| D-142 | **Staging host (H) — superseded (h5 B):** Do **not** provision dedicated staging DNS (`dev.*`, `staging.*`). Was **`https://dev.audiovau.lt`**; retired by [D-147](DECISIONS.md#infrastructure-and-operations) | — |
| D-133 | **Launch mailboxes (a4 all)** — **revised (E/A):** Before public beta promises, live **inbound** **`abuse@`** + **`privacy@`** + **`support@`** on iCloud; **outbound auth** via Resend **`noreply@`**. **No** **`export@`** until public beta | AV-T43 |
| D-135 | **Local-first implementation (h1 A):** Start **AV-T08** with untracked local `.env` and disposable fixtures **before** production infra ([AV-T21](TODO.md)); do not block scaffold on Servarica/R2 provision | AV-T08, PLAN Phase 2 |
| D-136 | **Local dev stack (h2 A):** Default agent/dev path uses **Docker Compose for Postgres** + **Cloudflare R2** (disposable dev bucket) for object storage — no local S3 container | AV-T08, VALIDATION.md |
| D-146 | **Public Pages before launch (h4 A):** Static site, PWA shell, and storage calculator may be served **publicly** on Cloudflare Pages (production apex and/or preview URLs) **before** full product launch ([D-03](DECISIONS.md#schedule-acceptance-and-capacity)). No private-only preview requirement; still no customer audio or secrets on static assets ([D-17](DECISIONS.md#infrastructure-and-operations)) | AV-T21, AV-T36 |
| D-147 | **Environment isolation (h5 B):** **No** separate staging DNS (`staging.*`, `dev.*`). Non-production behavior is selected by **`APP_ENV`** (`development` \| `staging` \| `production`) on the same public hostnames ([D-121](DECISIONS.md#web-platform-and-browsers)) or local Compose ([D-136](DECISIONS.md#infrastructure-and-operations)) | `APP_ENV` |
| D-149 | **Runtime app identity (h7 B):** **`APP_URL`** and **`APP_ENV`** are runtime-configurable via **`.env` only** — not hardcoded in source ([D-130](DECISIONS.md#web-platform-and-browsers) pattern) | `APP_URL`, `APP_ENV` |
| D-150 | **Freeze demo URL (h8 B):** End-of-September milestone demo may use a **Cloudflare Pages preview URL** (not production apex only); complements [D-146](DECISIONS.md#infrastructure-and-operations) | AV-T36 |
| D-151 | **Dev/staging API base URL (h9 — confirmed):** **Development:** local Go service at **`http://127.0.0.1:8080`** (`HTTP_ADDR=:8080`, [D-136](DECISIONS.md#infrastructure-and-operations)). **Staging:** same public hostname **`https://api.audiovau.lt`** as production; isolate via **`APP_ENV=staging`** + separate `.env` credentials/DB ([D-153](DECISIONS.md#infrastructure-and-operations)) — **no** staging API subdomain ([D-147](DECISIONS.md#infrastructure-and-operations)) | `API_PUBLIC_URL`, `HTTP_ADDR`, `APP_ENV` |
| D-153 | **Staging deploy model (§2 follow-up):** **Same Servarica host**, **different `.env` deploy** (`APP_ENV`, `DATABASE_URL`, R2 bucket/credentials). Only **one** backend configuration is live on **`https://api.audiovau.lt`** at a time — swap deploys sequentially; no parallel staging DNS ([D-147](DECISIONS.md#infrastructure-and-operations)) | `APP_ENV`, deploy process |
| D-155 | **Retire `*.dev.audiovau.lt` DNS (§2 follow-up):** Remove from Audio Vault product DNS import ([`config/dns/records.txt`](../../config/dns/records.txt)); superseded by [D-147](DECISIONS.md#infrastructure-and-operations). Review at **AV-T35** before **AV-T34** import | AV-T35, AV-T34 |
| D-156 | **Customer support email:** General product/billing help at **`support@audiovau.lt`** — **inbound-only** on Apple iCloud (same MX pattern as [D-143](DECISIONS.md#infrastructure-and-operations)); not used for auth outbound ([D-131](DECISIONS.md#infrastructure-and-operations)) | `SUPPORT_CONTACT_EMAIL`, AV-T43, [terms.md](../legal/terms.md) |

---

## Authentication and Sessions

| ID | Decision | Env |
|---|---|---|
| D-20 | Login methods (product): **password + magic link + passkeys** | `AUTH_ENABLE_*` |
| D-21 | Build order: **password → magic link → passkeys** | Implementation sequence |
| D-138 | **Passkeys at freeze (B):** **Disable passkeys through code freeze**; ship **post-freeze** ([D-21](DECISIONS.md#authentication-and-sessions)). Freeze auth: password + magic link ([D-144](DECISIONS.md#authentication-and-sessions)) | `AUTH_ENABLE_PASSKEY=false` until post-freeze |
| D-144 | **Magic link required at freeze (E):** End-of-September milestone requires working **magic-link login** (Resend outbound) in addition to password | AV-T08, D-02 |
| D-145 | **Magic-link TTL (E):** **30 minutes** | `AUTH_MAGIC_LINK_TTL_MINUTES=30` |
| D-152 | **Freeze auth methods (h10 A,B):** **Password** + **magic link** required at code freeze; **passkeys not** required ([D-138](DECISIONS.md#authentication-and-sessions)) | `AUTH_ENABLE_PASSWORD=true`, `AUTH_ENABLE_MAGIC_LINK=true`, `AUTH_ENABLE_PASSKEY=false` |
| D-22 | Sessions: **90-day rolling, 30-day idle** | `SESSION_*_DAYS` |
| D-23 | MFA: **optional** | `AUTH_MFA_MODE=optional` |
| D-24 | Recovery: **email password reset only** | No support-assisted recovery v1 |
| D-25 | Rate limits: **moderate** (5 login fails / 15 min / IP, etc.) | `AUTH_*`, `API_RATE_LIMIT_*` |
| D-26 | Logout: **session only** — upload/local state may remain until tab close | `LOGOUT_CLEAR_MODE=session_only` |

---

## Subscription, Stripe, and Launch Geography

| ID | Decision | Env |
|---|---|---|
| D-30 | **$49/month USD**, **$490/year USD** (~17% off) | `SUBSCRIPTION_PRICE*`, Stripe IDs |
| D-31 | Plan includes **2 TB** storage | `STORAGE_QUOTA_BYTES` |
| D-32 | **20 TB** engineering ceiling per account | `ACCOUNT_MAX_STORAGE_BYTES` |
| D-33 | **Luke 10 TB story** = evaluation only, not plan cap | AV-R22 interpretation |
| D-34 | Billing: **monthly + annual** | Stripe USD price IDs |
| D-35 | **USD-only plan prices**; sell worldwide via Stripe (no separate regional/EUR price IDs in app config) | `SUBSCRIPTION_CURRENCY=USD`, `STRIPE_PRICE_ID*` |
| D-36 | Trial: **14 days, card required** | `SUBSCRIPTION_TRIAL_DAYS` |
| D-37 | Failed payment: **7-day grace** — block uploads, allow playback, **no auto-delete** | `SUBSCRIPTION_*` |
| D-38 | Cancel: **Stripe portal, access until period end, no pro-rata** — **except mandatory statutory refunds/withdrawal** (see [terms.md](../legal/terms.md)) | `SUBSCRIPTION_CANCEL_*`, `SUBSCRIPTION_PRORATE_REFUND=false`, `SUBSCRIPTION_WITHDRAWAL_DAYS`, `SUBSCRIPTION_WITHDRAWAL_REIMBURSE_DAYS` |
| D-39 | **Stripe test mode** through freeze; live requires explicit authorization | `STRIPE_MODE=test`, `STRIPE_LIVE_REQUIRES_EXPLICIT_AUTH` |
| D-40 | Launch geography: **worldwide where Stripe supports** | `LAUNCH_GEO_MODE=stripe_supported` |
| D-41 | Prices and limits **configurable via `.env`**; account limit overrides per [D-128](DECISIONS.md#account-limits-and-settings) | See [CONFIG.md](CONFIG.md) |

---

## Uploads, Files, and Integrity

| ID | Decision | Env |
|---|---|---|
| D-50 | Max file **10 GB** | `UPLOAD_MAX_FILE_BYTES` |
| D-51 | Concurrency **default 3, max 6** | `UPLOAD_CONCURRENCY*` |
| D-52 | Resume window **30 days** | `UPLOAD_RESUME_TTL_DAYS` |
| D-53 | Orphan multipart cleanup **30 days** | `ORPHAN_MULTIPART_RETENTION_DAYS` |
| D-54 | Source identity: **SHA-256 + size + relative path** | Code (AV-T10) |
| D-55 | Path collision: **replace only if user confirms**; else reject | `UPLOAD_COLLISION_POLICY` |
| D-56 | Changed source: **overwrite after user confirm** | `UPLOAD_CHANGED_SOURCE_POLICY` |
| D-57 | Sidecars: **`.cue`, `.log`, cover images** — images catalog-only | `UPLOAD_ALLOW_*` |
| D-58 | Corrupt/zero-byte: **reject with reason, no auto-retry** | `UPLOAD_REJECT_CORRUPT` |
| D-59 | Paths: **preserve, max 4096 chars, NFC** | `UPLOAD_MAX_PATH_LENGTH`, `UPLOAD_PATH_NORMALIZE` |
| D-60 | Multipart part default **16 MiB** | `UPLOAD_PART_SIZE_BYTES` |
| D-61 | No NAS-retention messaging in UI | User responsibility (Q45-C) |
| D-123 | Upload scan accepts **music audio + approved sidecars only** ([D-57](DECISIONS.md#uploads-files-and-integrity)); **reject** all other file types with a visible reason (reinforces AV-R12 / chat [008](chat1.md#008--user)). Embedded file-tag metadata is part of the normal upload/catalog pipeline — **not** a third-party integration ([D-122](DECISIONS.md#legal-policies-and-accessibility)) | AV-T16, AV-T11 |

---

## Metadata and Privacy

| ID | Decision | Env |
|---|---|---|
| D-70 | Provider: **AcoustID + MusicBrainz** | `METADATA_PROVIDER` |
| D-71 | Auto-apply at **≥ 80%** confidence | `METADATA_CONFIDENCE_THRESHOLD=80` |
| D-72 | May send **filenames, tags, fingerprint** | `METADATA_SEND_*` |
| D-73 | Consent default: **privacy policy link at signup**; optional **`explicit_consent` mode** | `METADATA_CONSENT_MODE` |
| D-74 | Manual edits **always win**; **revert-to-auto allowed** | `METADATA_MANUAL_EDITS_ALLOW_REVERT` |

---

## Retention and Account Lifecycle

| ID | Decision | Env |
|---|---|---|
| D-80 | **30-day trash** | `TRASH_RETENTION_DAYS` |
| D-81 | Trash **counts toward quota** | `TRASH_COUNTS_TOWARD_QUOTA=true` |
| D-82 | Account deletion: **30-day soft delete, then purge**; wizard **cancels Stripe subscription first** ([D-134](DECISIONS.md#retention-and-account-lifecycle)) | `ACCOUNT_DELETION_RETENTION_DAYS`, `ACCOUNT_DELETION_CANCEL_STRIPE_FIRST` |
| D-134 | **Account deletion order (a5):** Multi-step delete wizard must **cancel Stripe subscription first** (API/portal), then start **30-day soft delete** ([D-82](DECISIONS.md#retention-and-account-lifecycle)); purge only after cancel confirmed and retention elapsed | AV-T08, AV-T19, AV-T23; `ACCOUNT_DELETION_CANCEL_STRIPE_FIRST` |

---

## Calculator and Economics

| ID | Decision | Tasks |
|---|---|---|
| D-90 | **Defer live price refresh** until after music MVP | AV-T04/T07 post-freeze |
| D-91 | Post-MVP: **daily automated** price feed job for the static calculator (not server `.env` at MVP) | AV-T07 post-MVP |
| D-92 | Calculator work **after music MVP**, not blocking freeze | Phase order |

---

## Web Platform and Browsers

| ID | Decision | Tasks |
|---|---|---|
| D-117 | **Browser minimum floors (defaults):** [Baseline Widely available on 2026-09-07](https://web.dev/articles/use-baseline-with-browserslist) — Chrome **121+**, Edge **121+**, Firefox **123+**, Safari / iOS Safari **17.4+**. Floors are **configurable** via `.env` ([D-130](DECISIONS.md#web-platform-and-browsers)) | [WEB.md](WEB.md), [`.browserslistrc`](../../.browserslistrc), AV-T05, AV-T12 |
| D-137 | **Freeze browser matrix (B):** Sign-off uses **latest stable** Chrome, Edge, Firefox, Safari on **desktop + mobile** (≥ [D-117](DECISIONS.md#web-platform-and-browsers) floors). **Block** unsupported UAs ([D-130](DECISIONS.md#web-platform-and-browsers) `block`) | AV-T05, AV-T12, AV-T18 |
| D-140 | **DSD in browser (B):** **Never** play DSD in-browser; show honest unsupported message + simulated streamer path ([D-110](DECISIONS.md#streamer-and-version-2)) | AV-T16, AV-T17 |
| D-130 | **Browser floors configurable:** Go API and PWA unsupported-browser gate read **`WEB_BASELINE_*`**, **`BROWSER_MIN_*`**, and **`BROWSER_UNSUPPORTED_POLICY`** from runtime config — **not** hardcoded version constants in source. Changing deploy floors updates `.env` (and [`.browserslistrc`](../../.browserslistrc) to match `WEB_BASELINE_QUERY` for build/CI). Material default changes still require a DECISIONS revision | AV-T05, AV-T12, AV-T18, AV-T08 |
| D-118 | **API CORS alignment:** `CORS_ALLOWED_ORIGINS` must match `APP_URL` (apex PWA); browser matrix tests use the same origin pair as production | [WEB.md](WEB.md), AV-T21, AV-T36, AV-T12 |
| D-148 | **CORS localhost in dev (h6 yes):** **`CORS_ALLOW_LOCALHOST=true`** when **`APP_ENV=development`** (local PWA → Compose API) | `CORS_ALLOW_LOCALHOST` |
| D-154 | **CORS Pages preview origins (§2 follow-up):** When freeze demo uses a Cloudflare Pages **preview URL** ([D-150](DECISIONS.md#infrastructure-and-operations)), **add that preview origin** to the API CORS allow list via **`CORS_PREVIEW_ORIGINS`** (comma-separated; merged with `CORS_ALLOWED_ORIGINS` at runtime). Production apex remains primary `CORS_ALLOWED_ORIGINS` ([D-118](DECISIONS.md#web-platform-and-browsers)) | `CORS_PREVIEW_ORIGINS` |
| D-119 | **Playback / streamer byte path:** **Direct short-lived R2 presigned URLs** with HTTP `Range` (browser + future real streamer adapter); Go issues presigns after auth — **no** bulk audio proxy through API except optional fallback if AV-T10 fails R2 CORS/range qualification. Freeze **simulated** streamer still needs no real hardware audio ([D-110](DECISIONS.md#streamer-and-version-2)) | AV-T10, AV-T11, AV-T17, AV-T25; `STORAGE_PRESIGN_MODE` |
| D-121 | **Public API hostname:** **`https://api.audiovau.lt`** — dedicated subdomain on Servarica origin, proxied through Cloudflare (**Full strict** TLS); apex (`APP_URL`) stays Pages/PWA-only ([D-16](DECISIONS.md#infrastructure-and-operations)–[D-17](DECISIONS.md#infrastructure-and-operations)); PWA `connect-src`, OpenAPI base URL, and Stripe Dashboard webhook URL use `API_PUBLIC_URL`; webhooks at **`/webhooks/stripe`**; liveness **`/health`**, readiness **`/ready`** | AV-T21, AV-T37, AV-T19; `API_PUBLIC_URL`, `STRIPE_WEBHOOK_PATH` |
| D-129 | **Browser verification:** AV-T05, AV-T12, and AV-T18 matrices must run in **real installed browsers** (Chrome, Firefox, Safari, Edge at ≥ [D-117](DECISIONS.md#web-platform-and-browsers) floors). Automation/headless harnesses are OK for CI; **freeze sign-off** requires real-browser rows (version recorded) or attached screen recordings — no UA-spoof-only sign-off | [WEB.md](WEB.md), AV-T05, AV-T12, AV-T18 |

---

## Legal, Policies, and Accessibility

| ID | Decision | Tasks |
|---|---|---|
| D-100 | Draft **privacy policy, terms of service, file-sharing policy** in repo; **counsel review before go-live** | AV-T40, AV-T41, AV-T42, AV-T22 |
| D-101 | Abuse/takedown contact: **`abuse@audiovau.lt`** | `ABUSE_CONTACT_EMAIL`, AV-T43 |
| D-102 | **WCAG 2.1 AA from first public beta**; best-effort OK for freeze demo | AV-T18 verification |
| D-116 | **Owner-only private library** — only the account holder may stream or retrieve their uploads; **no** sharing, public links, collaborative libraries, or distribution to other users or the public | [docs/legal/](../legal/README.md) AV-T40–T42; tenant isolation in AV-T08/AV-T11 |
| D-120 | **Account data export (A):** **No export** (self-service or manual email) **until public beta**. Defer **`export@audiovau.lt`** and export promises until beta | AV-T23, [privacy.md](../legal/privacy.md) |
| D-122 | **Integrations at freeze (A):** **No** third-party integrations in settings at freeze — **no** OAuth stub, **no** external catalog connectors. File-tag metadata from uploads remains in scope via normal upload/catalog pipeline ([D-123](DECISIONS.md#uploads-files-and-integrity), [D-70](DECISIONS.md#metadata-and-privacy)) | AV-T23; `INTEGRATIONS_*=false` |
| D-124 | **Account settings at freeze (A):** **Email/password** auth, **Stripe billing portal** link, **usage/quota** display, **account delete** flow ([D-134](DECISIONS.md#retention-and-account-lifecycle)), **policy links** (terms/privacy/content). **No** integration settings; **no** export UI/contact until beta ([D-120](DECISIONS.md#legal-policies-and-accessibility)) | AV-T08, AV-T23 |

---

## Account Limits and Settings

Interview: s6–s7 (Sep 7, 2026). Server `.env` sets deployment defaults and **hard ceilings**. Account settings may let the user tune each group **at or below** the server ceiling; overrides are **stored per account** in Postgres (not only in the browser).

| ID | Decision | Env / storage |
|---|---|---|
| D-125 | **Soak-test target (S):** **2 TB** total bytes, **100,000** files, **all supported formats** in [AV-R12](REQUIREMENTS.md) | [VALIDATION.md](VALIDATION.md#scale-limits-approved-sep-7-2026) fixed targets |
| D-126 | **UX uplink assumption (S):** **25 Mbps** upload, **single-stream** ETA model | `UX_TYPICAL_UPLINK_MBPS`, `UX_UPLINK_SINGLE_STREAM` |
| D-127 | **Max tracks/files (AV-T09 A):** **Configurable hard cap** — default **500,000** music files per account (`ACCOUNT_MAX_FILES`); reject new uploads at cap with visible error. **Not** a fixed code constant: server `.env` sets deploy default/ceiling; per-account override per [D-128](DECISIONS.md#account-limits-and-settings). **20** folder depth levels (`UPLOAD_MAX_PATH_DEPTH`); defaults align with **2 TB** plan quota ([D-31](DECISIONS.md#subscription-stripe-and-launch-geography)) and **20 TB** engineering ceiling ([D-32](DECISIONS.md#subscription-stripe-and-launch-geography)) | `ACCOUNT_MAX_FILES`, `UPLOAD_MAX_PATH_DEPTH`, `ACCOUNT_MAX_FILES_ENFORCEMENT` |
| D-128 | **Seven configurable limit groups** — each has a `.env` default **and** optional per-account user override (bounded by server ceiling): (1) storage quota bytes, (2) engineering max storage bytes, (3) max file count, (4) max path depth, (5) max single-file bytes, (6) upload concurrency default, (7) upload max concurrency | `ACCOUNT_LIMITS_USER_EDITABLE`, account DB columns |

---

## Streamer and Version 2

| ID | Decision | Phase |
|---|---|---|
| D-110 | **Simulated streamer sufficient** for end-of-Sept milestone | Phase 4 / AV-T17 |
| D-111 | **Defer DLC hardware** details | AV-T25 |
| D-112 | Phase 7: **quiet auto-connect when one authorized device** | `STREAMER_QUIET_CONNECT_SINGLE_DEVICE` |
| D-113 | Companion OS order: **macOS → Windows → Linux → NAS** | Phase 8 |
| D-114 | OS drive v2: **read-write** | AV-T31 |
| D-115 | **Mirroring out of v1 and initial v2** | AV-R43, AV-T33 |

---

## Explicitly Not Env-Configurable

These remain fixed in code, deployment layout, or documentation unless re-decided:

- Auth method **build order** (D-21)
- Cloudflare Pages vs Servarica **split** (D-16, D-17)
- WCAG and **legal review process** (D-102)
- Phase **7/8 sequencing** and DLC deferral (D-111–D-115)
- **BYO storage out of scope** (AV-T39)

---

## Revision History

| Date | Change |
|---|---|
| 2026-09-07 | **D-136 revised:** Compose **Postgres only**; **Cloudflare R2** (disposable dev bucket) for local object storage — no local S3 container |
| 2026-09-07 | **Chat 2 transcript:** [chat2-decisions.md](chat2-decisions.md) exported |
| 2026-09-07 | **D-01 revised:** Code freeze **September 10, 2026** (same day as launch); `CODE_FREEZE_DATE`, `PUBLIC_LAUNCH_DATE` |
| 2026-09-07 | **D-38:** Statutory withdrawal env keys; refund wording in [terms.md](../legal/terms.md) |
| 2026-09-07 | **D-03 revised:** Public launch **September 10, 2026 (US Eastern)** |
| 2026-09-07 | **CONFIG cleanup:** removed unused `PRICE_FEED_*`, `DLC_*`, `EXPORT_CONTACT_EMAIL`, `SOAK_TEST_*` keys |
| 2026-09-07 | **D-156:** Customer support at **`support@audiovau.lt`** (iCloud inbound) |
| 2026-09-07 | **D-153–D-155, D-151 confirmed:** staging same-host `.env` swap; CORS preview origins; retire `*.dev.audiovau.lt` DNS |
| 2026-09-07 | **D-147–D-152, D-142 superseded:** h5 B (`APP_ENV` only), h6 yes, h7 B, h8 B, h9 recommendation, h10 A+B |
| 2026-09-07 | **D-146:** Public Cloudflare Pages (apex/preview) before launch (h4 A) |
| 2026-09-07 | **B/S/A/H/E batch:** Browser freeze matrix, scale/soak, freeze settings, hosts/staging, Resend+iCloud mail — revises D-120, D-122, D-124, D-126, D-125, D-131, D-133; adds D-137–D-145, D-142–D-143 |
| 2026-09-07 | **D-135–D-136:** Local-first AV-T08; Docker Compose dev stack (maps to **H** implementation) |
| 2026-09-07 | **D-132–D-134:** AV-T24 acknowledgment (a3 A); all launch mailboxes (a4); Stripe cancel first on delete (a5) |
| 2026-09-07 | **D-131:** Auth email via **Resend** (E batch); supersedes prior iCloud-SMTP-only auth mail |
| 2026-09-07 | **D-130:** Browser floors configurable via `.env` / runtime config (not hardcoded) |
| 2026-09-07 | **D-127:** Configurable hard cap (default 500k files), not fixed in code |
| 2026-09-07 | **D-122–D-129:** s-batch scale/settings (integrations, soak, uplink, limits, browser matrices) — revised by B/S/A batch same day |
| 2026-09-07 | **D-121:** Public API hostname `https://api.audiovau.lt` (X4); webhook and health paths |
| 2026-09-07 | **D-118–D-120:** CORS/API alignment (X1); R2 presign playback path for browser/streamer (X2); defer self-service export, `export@audiovau.lt` (X3) |
| 2026-09-07 | **D-117:** Baseline Widely available browser floors (2026-09-07 pin); [WEB.md](WEB.md) |
| 2026-09-07 | **D-116:** Owner-only private library; legal drafts updated (no sharing or distribution) |
| 2026-09-07 | Initial interview decisions recorded; CONFIG.md and `.env.example` aligned |
| 2026-09-07 | **D-35 revised:** USD-only pricing worldwide via Stripe; removed EUR env vars |
| 2026-09-07 | [REQUIREMENTS.md](REQUIREMENTS.md) reconciled: AV-R* point here instead of duplicating state |

When revising a decision, update this file, [CONFIG.md](CONFIG.md), `.env.example` defaults, decision links in [REQUIREMENTS.md](REQUIREMENTS.md), and any affected AV-T evidence in [TODO.md](TODO.md).
