# Run402 Platform Updates > URL: https://run402.com/updates.txt > Format: Machine-readable changelog, newest first > See also: https://run402.com/humans/changelog.html (human-readable) --- ## 2026-04-14 - **Project soft-delete lifecycle**: Replaces the old 7-day silent destroy with a ~104-day state machine: `active → past_due → frozen → dormant → purged`. The **live site and end-user data plane keep serving throughout** — only the project owner's control plane (deploys, secret rotation, subdomain claims, function upload) gets gated. Three warning emails (past_due at day 0, frozen at day 14, final warning 24h before purge at day ~103). **Subdomain reservation** during grace: the name is reserved for the original owner's wallet so a missed renewal can't lose the brand to a squatter. **Inline reactivation**: any tier renewal/topup/upgrade during grace reactivates the project and clears all timers in the same transaction — the owner doesn't wait an hour for the scheduler to notice. Scheduled (cron) functions pause at day 44 to stop charging absent owners for compute. Pinned projects bypass the state machine entirely. New operator endpoints: `POST /projects/v1/admin/:id/reactivate` (admin rescue), `POST /subdomains/v1/admin/:name/release` (dispute resolution). **BREAKING for downstream consumers**: `DELETE /projects/v1/:id` response `status` field is now `"purged"` (was `"archived"`); mutating admin routes on grace-state projects return 402 `{ lifecycle_state, entered_state_at, next_transition_at }`; `@run402/shared` `ProjectStatus` union widened; `POST /subdomains/v1` can return 409 (reserved). Legacy `archived` rows preserved as-is. Motivated by saas-factory products: one missed renewal used to silently destroy a live brand's data + subdomain with no recourse — now it takes ~104 days, three emails, and the brand name is held until the tail expires. ## 2026-04-10 - **Custom domain inbound email**: Verified custom sender domains can now receive inbound email, not just send. Enable via `POST /email/v1/domains/inbound` with `{ "domain": "yourdomain.com" }` — returns the MX record to add to your DNS. Replies to `@yourdomain.com` route through the same inbound pipeline as `@mail.run402.com`. Opt-in, requires DKIM-verified domain. Disable with `DELETE /email/v1/domains/inbound`. Removing the sender domain cascades to disable inbound first. Motivated by kysigned's reply-to-sign flow — signers reply to `reply-to-sign@kysigned.com` rather than `@mail.run402.com`. ## 2026-04-09 - **Raw inbound MIME accessor**: New `GET /mailboxes/v1/:id/messages/:messageId/raw` endpoint returns the exact RFC-822 bytes of an inbound message — fetched verbatim from S3 with no parsing, normalization, or modification. Inbound messages only; same `service_key` auth as the JSON message endpoint. `Content-Type: message/rfc822`, 10MB cap. Use this for cryptographic verification (DKIM signature checks, zk-email proofs, archival-grade email storage). The existing JSON endpoint still returns parsed `body_text` for display and threading — unchanged. Motivated by kysigned's reply-to-sign signing flow, where a zk-email circuit proves a DKIM-signed reply attests "I APPROVE" — any post-processing breaks the cryptographic chain. ## 2026-04-06 - **KMS contract wallet**: New `/contracts/v1/*` endpoints for AWS KMS-backed Ethereum wallets per project. Provision via `POST /contracts/v1/wallets` (chain: `base-mainnet` or `base-sepolia`); private keys never leave KMS. Pricing: **$0.04/day** per wallet ($1.20/month, billed daily as `kms_wallet_rental` ledger entries) plus **$0.000005 per contract call** (KMS sign fee, billed alongside chain gas at-cost). Wallet creation requires $1.20 prepay (30 days of rent) in cash credit. Submit write calls via `POST /contracts/v1/call` with `{ wallet_id, contract_address, abi_fragment, function_name, args }`; idempotent on `Idempotency-Key` header. Status via `GET /contracts/v1/calls/:id`. Read-only calls via `POST /contracts/v1/read` (no signing, no billing). Suspension is automatic when cash balance can't cover rent; reactivation is automatic on top-up. Wallets that stay suspended for 90 days have their KMS key destroyed (7-day AWS deletion window). Funds-rescue mechanisms: drain endpoint (`POST /contracts/v1/wallets/:id/drain` with `X-Confirm-Drain` header — works on suspended wallets), recovery address (auto-drain on day-90 deletion), low-balance alerts. **Non-custodial**: run402 provides KMS-backed signing, not fund custody — see https://run402.com/humans/terms.html for the disclaimer. - **Email billing accounts + Stripe tier checkout + email packs**: App owners can now create billing accounts with just an email (Stripe-only, no wallet required) via `POST /billing/v1/accounts`. Subscribe/renew/upgrade tiers via credit card at `POST /billing/v1/tiers/:tier/checkout` (supports both wallet and email identifiers). High-volume email sending via $5 email packs at `POST /billing/v1/email-packs/checkout` — each pack = 10,000 emails, never expire, require a verified custom sender domain to consume (`mail.run402.com` stays hard-capped at tier limits for spam protection). Auto-recharge optional via `POST /billing/v1/email-packs/auto-recharge`. Balance and history endpoints auto-detect wallet (0x...) or email (@) identifiers. Link a wallet to an email account later for hybrid x402+Stripe access. Full backward compat: all existing wallet-based flows unchanged. T1 scope only — no end-user charging (T2 deferred, see docs/ideas.md). ## 2026-04-05 - **Custom sender domains**: Register a custom email sending domain (e.g., `kysigned.com`) via `POST /email/v1/domains`. DKIM verification via SES, DNS records provided. Wallet-scoped ownership for multi-project reuse. Falls back to `@mail.run402.com` when unverified. - **Magic link auth**: Passwordless email authentication for project users. Request a magic link via `POST /auth/v1/magic-link`, verify via `grant_type=magic_link`. Auto-creates users on first use. Includes password change/reset/set endpoint (`PUT /auth/v1/user/password`) and project-level `allow_password_set` setting. Multi-method identity model: users can now have any combination of password, OAuth, and magic link auth. ## 2026-04-02 - **Extract functions package**: Extracted `@run402/functions` runtime helper into standalone TypeScript npm package with full type definitions, replacing inlined heredoc in Lambda layer ## 2026-03-31 - **Public storage URLs**: Added unauthenticated public read route for storage files; public URL included in upload responses - **Incremental deploy**: Added `inherit: true` flag on deploy requests to carry forward unchanged files from previous deployment via S3 server-side copy - **Auto Worker custom domains**: Automatic Cloudflare Worker Custom Domain binding creation/deletion when custom domains are registered or released via API - **Domain status auth**: Added `serviceKeyOrAdmin` middleware to domain status endpoint, closing security gap where project data was publicly queryable ## 2026-03-30 - **Project admin role**: Added `project_admin` Postgres role with BYPASSRLS, `is_admin` flag on users, admin JWT issuance, and promote/demote endpoints for app-level admin access ## 2026-03-29 - **on-signup hook**: Introduced `on-*` lifecycle hook convention; gateway auto-invokes `on-signup` function fire-and-forget after first user signup - **Bundle deploy error detail**: Fixed error swallowing in bundle deploy so FunctionError, DeploymentError, and unknown errors propagate message and status to client ## 2026-03-28 - **Full email**: Added raw HTML send mode, display name (`from_name`) support, bumped team tier daily send limit to 500 - **Search Console coverage fix**: Fixed 404s and missing sitemap entries by adding directory index pages, updating sitemap.xml, and custom 404 page ## 2026-03-26 - **Scheduled functions**: Added cron-based scheduled invocation of deployed functions with deploy-time schedule config, manual trigger, execution metadata, and tier limits ## 2026-03-25 - **Secrets value hash**: Added truncated SHA-256 hash to secrets list response so agents can verify secret values without exposing them - **Input validation hardening**: Centralized input validation (UUID, wallet, email, slug, URL) for all route handlers with clean 400 responses ## 2026-03-24 - **Project email (Phase 1)**: Per-project mailboxes at `@mail.run402.com` with template-based outbound, reply-only inbound, tier-based limits, and SES integration - **Branded SQL type**: Introduced branded `SQL` type with `sql()` helper and libpg-query pre-flight validation - **Admin auth and operations**: Admin auth middleware (ADMIN_KEY, SIWx, OAuth, service_key) with admin override on ownership-gated endpoints - **Test coverage**: Added c8 coverage instrumentation for gateway unit tests with CI integration ## 2026-03-23 - **TypeScript type stripping**: Added esbuild transpilation so edge functions written in TypeScript deploy without SyntaxErrors - **Cascade project delete**: Extended project archival to cascade-delete all owned resources (Lambda, secrets, subdomains, S3, deployments, app versions) - **Bootstrap function**: Convention-based `bootstrap` function auto-invoked after fork/deploy with caller-provided variables - **getUser() helper**: Added `getUser(req)` to functions runtime to retrieve authenticated user inside edge functions via JWT