Skip to content

Commit 27f849e

Browse files
committed
feat(launch): add Autumn production parity dry run
1 parent 29cf94b commit 27f849e

14 files changed

Lines changed: 2251 additions & 30 deletions

.env.example

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ REND_BILLING_FEATURE_STORAGE_720P=storage_720p_second_months
3333
REND_BILLING_FEATURE_STORAGE_1080P=storage_1080p_second_months
3434
REND_BILLING_FEATURE_STORAGE_2K=storage_2k_second_months
3535
REND_BILLING_FEATURE_STORAGE_4K=storage_4k_second_months
36+
REND_AUTUMN_USAGE_CREDIT_FEATURE_ID=rend_usage_credits
37+
REND_AUTUMN_PLAN_PAYG_ID=pay_as_you_go
38+
REND_AUTUMN_PLAN_BUILDER_ID=builder
39+
REND_AUTUMN_PLAN_SCALE_ID=scale
40+
REND_AUTUMN_PLAN_ENTERPRISE_ID=enterprise
3641
REND_BILLING_ENTITLEMENT_FAILURE_POLICY=fail_closed
3742
REND_BILLING_DELIVERY_SYNC_LAG_SECS=60
3843
REND_BILLING_DELIVERY_SYNC_MAX_WINDOW_SECS=3600

.env.local.example

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ REND_BILLING_FEATURE_STORAGE_720P=storage_720p_second_months
3939
REND_BILLING_FEATURE_STORAGE_1080P=storage_1080p_second_months
4040
REND_BILLING_FEATURE_STORAGE_2K=storage_2k_second_months
4141
REND_BILLING_FEATURE_STORAGE_4K=storage_4k_second_months
42+
REND_AUTUMN_USAGE_CREDIT_FEATURE_ID=rend_usage_credits
43+
REND_AUTUMN_PLAN_PAYG_ID=pay_as_you_go
44+
REND_AUTUMN_PLAN_BUILDER_ID=builder
45+
REND_AUTUMN_PLAN_SCALE_ID=scale
46+
REND_AUTUMN_PLAN_ENTERPRISE_ID=enterprise
4247
REND_BILLING_ENTITLEMENT_FAILURE_POLICY=fail_closed
4348
REND_BILLING_DELIVERY_SYNC_LAG_SECS=60
4449
REND_BILLING_DELIVERY_SYNC_MAX_WINDOW_SECS=3600

.env.production.example

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ AWS_ACCESS_KEY_ID=replace-me
2424
AWS_SECRET_ACCESS_KEY=replace-me
2525

2626
REND_API_BASE_URL=https://api.example.com
27+
REND_PUBLIC_API_BASE_URL=https://api.rend.so
28+
REND_PUBLIC_SITE_BASE_URL=https://rend.so
2729
REND_API_BIND_ADDR=0.0.0.0:4000
2830
REND_API_AUTO_MIGRATE=true
2931
REND_API_INLINE_MEDIA_PROCESSING=false
@@ -40,6 +42,11 @@ REND_BILLING_FEATURE_STORAGE_720P=storage_720p_second_months
4042
REND_BILLING_FEATURE_STORAGE_1080P=storage_1080p_second_months
4143
REND_BILLING_FEATURE_STORAGE_2K=storage_2k_second_months
4244
REND_BILLING_FEATURE_STORAGE_4K=storage_4k_second_months
45+
REND_AUTUMN_USAGE_CREDIT_FEATURE_ID=rend_usage_credits
46+
REND_AUTUMN_PLAN_PAYG_ID=pay_as_you_go
47+
REND_AUTUMN_PLAN_BUILDER_ID=builder
48+
REND_AUTUMN_PLAN_SCALE_ID=scale
49+
REND_AUTUMN_PLAN_ENTERPRISE_ID=enterprise
4350
REND_BILLING_ENTITLEMENT_FAILURE_POLICY=fail_closed
4451
REND_BILLING_DELIVERY_SYNC_LAG_SECS=60
4552
REND_BILLING_DELIVERY_SYNC_MAX_WINDOW_SECS=3600
@@ -110,6 +117,7 @@ BETTER_AUTH_URL=https://app.example.com
110117
RESEND_API_KEY=replace-me
111118
REND_AUTH_EMAIL_FROM=Rend <auth@example.com>
112119
REND_AUTH_EMAIL_DISABLED=false
120+
REND_OPERATOR_EMAIL_ALLOWLIST=ops@example.com
113121
REND_SITE_MAX_UPLOAD_BYTES=536870912
114122
NEXT_PUBLIC_REND_APP_VERSION=production
115123

.github/workflows/release-deploy.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,9 @@ jobs:
141141
REND_EDGE_AMS_SSH_HOST: ${{ secrets.REND_EDGE_AMS_SSH_HOST }}
142142
REND_EDGE_AMS_SSH_USER: ${{ secrets.REND_EDGE_AMS_SSH_USER }}
143143
REND_EDGE_AMS_SSH_PORT: ${{ secrets.REND_EDGE_AMS_SSH_PORT }}
144+
AUTUMN_SECRET_KEY: ${{ secrets.AUTUMN_SECRET_KEY }}
145+
AUTUMN_API_URL: ${{ vars.AUTUMN_API_URL || 'https://api.useautumn.com/v1' }}
146+
AUTUMN_API_VERSION: ${{ vars.AUTUMN_API_VERSION || '2.3.0' }}
144147
steps:
145148
- name: Check out repository
146149
uses: actions/checkout@v4
@@ -184,6 +187,17 @@ jobs:
184187
chmod 600 "$HOME/.ssh/known_hosts"
185188
echo "REND_SSH_KEY_PATH=$SSH_KEY_PATH" >> "$GITHUB_ENV"
186189
190+
- name: Sync control-plane billing env
191+
if: env.DEPLOY_CONTROL_PLANE == 'true'
192+
run: |
193+
set -euo pipefail
194+
: "${AUTUMN_SECRET_KEY:?AUTUMN_SECRET_KEY Production environment secret is required}"
195+
196+
scripts/sync-control-plane-billing-env-over-ssh.sh \
197+
--host "$REND_CONTROL_PLANE_SSH_HOST" \
198+
--user "$REND_CONTROL_PLANE_SSH_USER" \
199+
--port "${REND_CONTROL_PLANE_SSH_PORT:-22}"
200+
187201
- name: Deploy control plane
188202
if: env.DEPLOY_CONTROL_PLANE == 'true'
189203
run: |

docs/billing-autumn-v1.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ REND_BILLING_FEATURE_STORAGE_720P=storage_720p_second_months
5757
REND_BILLING_FEATURE_STORAGE_1080P=storage_1080p_second_months
5858
REND_BILLING_FEATURE_STORAGE_2K=storage_2k_second_months
5959
REND_BILLING_FEATURE_STORAGE_4K=storage_4k_second_months
60+
REND_AUTUMN_USAGE_CREDIT_FEATURE_ID=rend_usage_credits
6061
```
6162

6263
The Autumn dev/sandbox catalog should include:
@@ -69,6 +70,51 @@ The Autumn dev/sandbox catalog should include:
6970
Overages should be billed by Autumn/Stripe. Rend does not hardcode plan prices
7071
except display fallback copy.
7172

73+
The public V1 production plan IDs must be:
74+
75+
```sh
76+
REND_AUTUMN_PLAN_PAYG_ID=pay_as_you_go
77+
REND_AUTUMN_PLAN_BUILDER_ID=builder
78+
REND_AUTUMN_PLAN_SCALE_ID=scale
79+
REND_AUTUMN_PLAN_ENTERPRISE_ID=enterprise
80+
```
81+
82+
`rend_usage_credits` is the Autumn credit-system feature attached to those
83+
plans. It is not a Stripe object managed by Rend.
84+
85+
## Sandbox and Live Separation
86+
87+
Sandbox and production Autumn keys must never be loaded into the same runtime as
88+
the active `AUTUMN_SECRET_KEY`. The production launch gate requires the live
89+
Autumn key to come from `.env.production.local`, and the key must be visibly
90+
marked as live. The sandbox parity input should be a separate env file, usually
91+
`.env.local`, containing a visibly marked test/sandbox Autumn key.
92+
93+
Read-only parity check:
94+
95+
```sh
96+
bun run billing:autumn-parity -- \
97+
--sandbox-env-file .env.local \
98+
--production-env-file .env.production.local
99+
```
100+
101+
This fetches only the required Autumn features and plans, compares production to
102+
sandbox, verifies the credit-system schema and plan attachments, and writes a
103+
redacted artifact under `.rend/launch/`. It does not copy customers,
104+
subscriptions, or Stripe objects.
105+
106+
If production parity fails because live Autumn catalog objects are missing,
107+
upsert the catalog through Autumn with the explicit mutation flag:
108+
109+
```sh
110+
node scripts/with-root-env.mjs --profile production --env-file .env.production.local \
111+
node scripts/setup-autumn-billing.mjs --plans --mux-basic-rates --allow-production-mutation
112+
```
113+
114+
This command may create or update Autumn products/plans and the Stripe live
115+
objects Autumn manages for them. It must not be replaced with manual Stripe
116+
product or price creation.
117+
72118
Use the setup helper to upsert the required feature IDs:
73119

74120
```sh
@@ -177,3 +223,37 @@ POST /internal/operator/billing/delivery-sync
177223
Use the dashboard operator billing panel to inspect customer sync status and
178224
manually resync an organization. Operators must not mutate plan state directly
179225
in Rend; plan/product changes should go through Autumn.
226+
227+
## Production Dry Run
228+
229+
Run the production dry run only after parity and the production launch gate pass:
230+
231+
```sh
232+
bun run launch:gate -- --mode production-check --autumn-sandbox-env-file .env.local
233+
234+
bun run launch:production-dry-run -- \
235+
--allow-production-mutation \
236+
--acknowledge-real-charge \
237+
--plan-id pay_as_you_go
238+
```
239+
240+
Before running it, deploy or restart the production API on the current build and
241+
confirm migrations through `0011_billing_storage_spans.sql` are applied. The dry
242+
run intentionally fails if upload does not create the `upload_gate` billing
243+
event or if storage/delivery usage does not track through Autumn.
244+
245+
`--acknowledge-real-charge` is required for every production dry run because
246+
tracked usage can create live Autumn/Stripe billing artifacts even on
247+
`pay_as_you_go`. Document the intended plan and expected charge before running
248+
it. The dry run creates or syncs the internal customer
249+
`Rend Internal Production Dry Run`, attaches the requested Autumn plan, creates a
250+
scoped live API key, uploads a synthetic fixture through the public API, waits
251+
for playable media, verifies upload check plus delivery/storage usage tracking,
252+
verifies checkout/portal URL creation, verifies public embed/watch playback
253+
through the edge, deletes the asset, revokes the generated API key, and writes a
254+
redacted artifact under `.rend/launch/`.
255+
256+
The dry run intentionally leaves the Autumn customer, plan relationship,
257+
balances, usage, and Autumn-generated Stripe objects visible for dashboard
258+
inspection. It must not be used to create structural Stripe products or prices
259+
outside Autumn.

docs/deployment-v1.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,11 @@ Worker:
148148
- all API dependency vars used for Postgres, Redis, S3, ClickHouse, playback
149149
signing, edge internal auth, and Autumn billing
150150
- `REND_API_AUTO_MIGRATE=false` after the API migration step is deployed
151+
152+
The `Release and Deploy Backend` workflow syncs the billing-only allowlist into
153+
the control-plane API and worker env files before deployment. The Production
154+
GitHub environment must include `AUTUMN_SECRET_KEY`; the sync helper refuses to
155+
run unless it is visibly a live Autumn key, and logs only key names.
151156
- `REND_MEDIA_WORKER_ID`
152157
- `REND_MEDIA_WORKER_POLL_INTERVAL_SECS`
153158
- `REND_MEDIA_JOB_LOCK_TIMEOUT_SECS`

docs/env/profiles.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ instead of committing env files. `REND_ENV` must be `production`.
5555
`REND_BILLING_MODE` must be `autumn`, and `AUTUMN_SECRET_KEY` must be provided
5656
server-side only.
5757

58+
Autumn production launch checks require the live `AUTUMN_SECRET_KEY` to be in
59+
`.env.production.local`. Keep sandbox/test Autumn keys in a separate file such as
60+
`.env.local` and pass that file to catalog parity with
61+
`--autumn-sandbox-env-file`; do not export a sandbox `AUTUMN_SECRET_KEY` in the
62+
shell while running production-check.
63+
5864
Production validation rejects localhost/Docker service URLs, checked-in dev
5965
defaults, placeholders, insecure edge URLs, and `REND_ENV=local`.
6066

docs/launch-gate-v1.md

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,22 +36,27 @@ Production-check mode validates real production env/config without mutating live
3636
billing:
3737

3838
```sh
39-
bun run launch:gate -- --mode production-check
39+
bun run launch:gate -- --mode production-check --autumn-sandbox-env-file .env.local
4040
```
4141

42+
Production-check requires `.env.production.local` to supply the live Autumn key
43+
used as `AUTUMN_SECRET_KEY`; inherited shell keys are rejected when they differ.
4244
Use `--production-env-file FILE` to validate a specific production env file from
43-
any mode. Production-check skips mutating live/API smoke by default. Only pass
44-
`--allow-live-billing-mutation` when an operator intentionally wants to call
45-
Autumn `customers.get_or_create` against the configured production account.
45+
any mode, but the Autumn live key check remains tied to `.env.production.local`.
46+
Production-check also runs read-only Autumn sandbox/live catalog parity using
47+
`--autumn-sandbox-env-file` and `--autumn-production-env-file`. It skips
48+
mutating live/API smoke by default. Only pass `--allow-live-billing-mutation`
49+
when an operator intentionally wants to call Autumn `customers.get_or_create`
50+
against the configured production account.
4651

4752
## Required Checks
4853

4954
The gate reports explicit `pass`, `warn`, or `fail` status for each group:
5055

5156
- env: `env:local`, `env:production:example`, loaded profile policy, and
5257
production env validation when supplied.
53-
- billing: launch mode policy, Autumn catalog expectations, customer mapping,
54-
and billing denial smoke.
58+
- billing: launch mode policy, Autumn catalog expectations, sandbox/live
59+
catalog parity, customer mapping, and billing denial smoke.
5560
- OpenAPI: lint/generated client check and public contract tests.
5661
- SDK: unit tests and integration smoke.
5762
- site: tests, typecheck, build, and E2E flows.
@@ -97,8 +102,11 @@ Common failures:
97102
- `production-env-validation`: replace placeholders, remove `REND_DEV_API_KEY`,
98103
set secure Better Auth config, require `REND_BILLING_MODE=autumn`, and set an
99104
operator email allowlist.
100-
- `autumn-catalog`: run the sandbox setup helper or fix the configured plan and
101-
feature IDs before re-running the gate.
105+
- `autumn-catalog`: run the sandbox setup helper or fix the configured plan,
106+
usage-credit feature, and meter feature IDs before re-running the gate.
107+
- `autumn-catalog-parity`: make production match the verified sandbox catalog in
108+
Autumn. Do not copy customers/subscriptions and do not create Stripe
109+
products/prices directly outside Autumn.
102110
- `site:build`: production-profile env is missing or incompatible with the
103111
Next.js build.
104112
- `docker:up` or `docker:smoke`: inspect Docker Compose health and service logs,

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
"env:production:example": "node scripts/validate-env.mjs --profile production --env-file .env.production.example --allow-placeholders",
1919
"verify:production-local": "bun run env:production && REND_ENV_PROFILE=production turbo build --filter=@rend/site && REND_ENV_PROFILE=production turbo typecheck --filter=@rend/site && cargo check --workspace",
2020
"launch:gate": "node scripts/launch-gate.mjs",
21+
"billing:autumn-parity": "node scripts/autumn-catalog-parity.mjs",
22+
"launch:production-dry-run": "bun scripts/production-dry-run.mjs",
2123
"backend:up": "bun run env:local && REND_ENV_PROFILE=local docker compose up -d",
2224
"backend:down": "docker compose down",
2325
"backend:api": "bun run env:local && REND_ENV_PROFILE=local cargo run -p rend-api",

0 commit comments

Comments
 (0)