Changelog

Release archive

Older Pushify releases. The latest updates live on the main changelog.

v0.2.0-beta.59

Platform & API

Added

  • Notification preferences are now real. They lived only in the browser's localStorage — the backend never saw them. New users.notification_prefs (migration 0038) with GET/PUT /auth/me/notification-prefs, which also exposes the onboarding-email opt-out as a toggle. Two preferences gained actual consumers immediately: securityAlerts now gates the new-device sign-in email, and weeklyDigest powers a brand-new weekly digest worker — Mondays (UTC), opt-in only, real per-organization numbers (deployments and failures this week, active projects, running servers, credit balance), atomic per-week dedupe, and skipped entirely when there is nothing to report. deploymentAlerts/productUpdates are stored and ready for their future senders.

v0.2.0-beta.58

Platform & API

Added

  • Onboarding email sequence (state-driven, not a dumb timer). A new hourly worker walks organizations created in the last 30 days and sends at most one lifecycle email per state: ~day 1 "deploy your first app" (only if they haven't), ~day 3 either "need a hand?" (still no deploy) or "connect a domain" (deployed, no custom domain), day 7 "add a database" (deployed, no DB). Each email links a signed unsubscribe URL (GET /auth/unsubscribe-onboarding?token=) that sets a per-user opt-out honored by the whole sequence; sends are claimed atomically in a new onboarding_emails table (unique per org+email) so concurrent sweeps can never double-send, and failed sends retry next sweep. The 30-day cap guarantees existing users are never spammed at rollout. Migration 0038* — tables onboarding_emails, cancellation_feedback, column users.onboarding_emails_opt_out. 6 unit tests on the state machine.
  • Cancellation exit survey — POST /billing/cancellation-feedback records a one-question reason (too_expensive | missing_features | bugs | switched | project_ended | other + optional comment) and notifies the operator (feedback.cancellation admin event). Never blocks the cancel flow.

v0.2.0-beta.57

Platform & API

Fixed

  • OAuth (Google/GitHub) accounts can now manage 2FA and set a password. Accounts without a password hit dead ends on every password-confirmation flow. Now: /auth/me exposes hasPassword; disabling 2FA and regenerating backup codes accept either the account password or (for passwordless accounts) a current authenticator/backup code via the shared re-auth guard; and /auth/me/change-password lets a passwordless account set its first password without currentPassword (the authenticated session is the proof) — password accounts still verify the current password and the not-same-as-old rule.
  • 2FA disable/backup-code regeneration was broken for everyone: the password check passed its arguments to verifyPassword in the wrong order, so the correct password always failed verification. Fixed alongside the guard rework.

v0.2.0-beta.56

Platform & API

Added

  • Full domain management for sold domains. Customers' domains live in Pushify's reseller account, so the platform is their only control panel — this release makes it a complete one: - DNS records — list/create/update/delete A, AAAA, CNAME, MX, TXT, SRV, NS records (host/TTL/priority validation) via /domains/:domain/dns. - Domain transfer-in — GET /domains/transfer/quote prices a transfer at the TLD's renewal rate (probed live from the registrar); POST /domains/transfer charges the wallet, starts the transfer with the auth/EPP code (refund if it fails to start), and records it as transfer_pending (migration 0036). The renewal worker now also polls in-flight transfers every sweep: completed → domain becomes active with its real expiry; cancelled/rejected → automatic refund + status transfer_failed. Start/result emails (EN/TR) + operator events. - Transfer-out (ICANN compliance) — POST /domains/:domain/auth-code unlocks the domain and returns its EPP code so users can leave freely; viewing it triggers a security notice email to the owner and an operator event. - Registrar lock toggle and custom nameservers (2-6, validated) — point a domain at Cloudflare or any external DNS. - Email forwarding — [email protected] → anywhere aliases (list/create/delete). - Public availability search — GET /domains/public-search (no auth, 10 req/min/IP) to power a marketing domain-search page.

v0.2.0-beta.55

Platform & API

Added

  • Multi-year domain registration (1–5 years). POST /domains/purchase and the new quote logic accept a years term; the total is priced as year-1 registration + (years−1) renewals on both the wholesale and retail side, so multi-year never undercuts cost. Term is stored per domain and reflected in emails/admin events.
  • Post-redirect purchase confirm — POST /domains/purchase/confirm fulfills a paid checkout session directly when the user returns from Stripe (org-verified, idempotent with the webhook per session id), so domains register instantly even before the webhook lands — and local/dev setups work without stripe listen.
  • Pay by card when credits don't cover a domain. New POST /domains/purchase/checkout creates a Stripe Checkout session for the exact quoted amount; on checkout.session.completed the webhook credits the wallet with the paid amount (idempotent per session id) and registers the domain through the normal purchase path. If registration fails after payment, the paid amount stays as wallet credit (never lost) and the operator is notified.

v0.2.0-beta.53

Dashboard

Added

  • Domains page (/dashboard/domains, pairs with backend beta.54): search a name or keyword and see availability + prices across 10 popular TLDs, buy in one click (paid from infrastructure credits), and optionally connect the domain to a project during checkout — DNS records and SSL are set up automatically. Purchased domains list shows expiry, attached project, renewal problems, and a per-domain auto-renew toggle. New Domains item in the sidebar (Globe icon). When the platform has no registrar configured, the page shows a quiet "not enabled" note instead. EN/TR i18n.

v0.2.0-beta.54

Platform & API

Added

  • Domain sales (registrar reseller integration). Users can now search, buy, and auto-connect custom domains without leaving Pushify: - Registrar adapter layer (REGISTRAR_PROVIDER=namecom + NAMECOM_USERNAME/NAMECOM_TOKEN, optional NAMECOM_API_URL for name.com's test environment). The adapter interface is provider-agnostic so higher-volume wholesalers (OpenSRS/CentralNic) can be added later without touching the product layer. Unset = feature hidden everywhere. - Retail pricing with margin — wholesale price + DOMAIN_MARGIN_PERCENT (default 20%), rounded up to a x.49/x.99 ending, never below cost; DOMAIN_MAX_PRICE_CENTS (default $300) and a premium-domain block guard against expensive surprises. - API: GET /api/v1/domains/config (feature discovery), GET /domains/search?q= (availability + retail prices across 10 popular TLDs), POST /domains/purchase, GET /domains, PATCH /domains/:domain/auto-renew. - Payment from infra credits: purchase debits the wallet (new domain_purchase/domain_renewal transaction types); the charge is taken first and automatically refunded if registration fails. WHOIS privacy is enabled on registration. - Auto-connect to a project: optional projectId creates apex A + www CNAME records at the registrar pointing at the project's server and registers the domain on the project (existing verify → nginx → SSL flow takes over). Best-effort — a DNS/attach hiccup never voids the purchase. - Renewal worker (12h sweep): domains expiring within 30 days auto-renew from the wallet at the registrar's live renewal price (falls back to the price captured at purchase), refund on failure; insufficient credits / auto-renew-off / failures send the owner a reminder email (throttled to one per 7 days); past-expiry domains are marked expired. New tables: purchased_domains (migration 0035). - Emails + admin events: purchase/renewal confirmations and renewal reminders (EN/TR); domain.purchased, domain.renewed, domain.renewal_failed operator notifications.

v0.2.0-beta.53

Platform & API

Fixed

  • Public port no longer changes on every compose-stack redeploy. Marketplace stacks (Supabase, Cal.com, Appwrite…) re-scanned for a "free" port on each deploy while the previous stack was still running — so the stack saw its own port as busy and shifted to a new port (and a new URL) every redeploy. The port is now sticky: the server-side port registry (and, for stacks deployed before this fix, the PUSHIFY_PUBLIC_PORT recorded in the stack's .env) is reused as long as the project's own containers hold the port or it is otherwise free; a brand-new port is picked only on first deploy or if another process took the old one while the stack was down.
  • New port assignments now avoid every genuinely busy port. The used-port scan only matched 127.0.0.1: Docker bindings, but app containers publish on 0.0.0.0 — so the scan saw almost nothing, and host daemons (user services, databases) weren't checked at all. Assignment now skips all Docker-published host ports and all host TCP listeners, and a registry entry squatted by a foreign process is released and reassigned instead of producing a doomed docker run. Ownership checks are exact (pushify-<slug>, its -blue/-green variants, or the compose project label) so project app can never claim app-2's port. 6 unit tests.

v0.2.0-beta.52

Platform & API

Fixed

  • Custom env vars now reach Supabase (and other compose) containers. Adding e.g. GOTRUE_EXTERNAL_GOOGLE_SKIP_NONCE_CHECK in a Supabase project's Environment tab wrote it to the stack's .env, but Docker Compose only injects variables explicitly listed under a service's environment: — so the value never appeared inside the GoTrue container. Marketplace templates can now declare envPassthrough (service → env-key prefixes); at deploy time a docker-compose.override.yml is generated that forwards matching user vars to the right service (user values win over template defaults on collision; stale overrides are removed). The Supabase template forwards GOTRUE_* → auth and PGRST_* → rest, unlocking all GoTrue/PostgREST tuning knobs. Redeploy required after changing env vars, as before. 5 unit tests.

v0.2.0-beta.52

Dashboard

Added

  • Invoices in Billing. New section on the Billing page (pairs with backend beta.50): your Stripe invoice history with number, date, status chip, amount, a hosted View link and PDF download. Hidden until the organization has invoices. EN/TR i18n.

v0.2.0-beta.51

Dashboard

Added

  • SECURITY.md — vulnerability disclosure policy (matching the backend's).
  • Real product screenshots in the README — the redesigned landing hero and the dashboard preview, captured at 2× from the live page, stored under .github/assets/.

v0.2.0-beta.50

Dashboard

Changed

  • Navbar scroll animation + mobile audit. The capsule navbar now reacts to scroll: quiet and airy at the top (h-14, soft shadow), it condenses smoothly on scroll (h-12, closer to the edge, more opaque, deeper shadow) with a 300ms transition — the rAF-throttled listener is passive, so scrolling stays smooth. A full 390px-wide sweep of the homepage (8 scroll depths, plus footer/CTA) confirmed the responsive layout is clean end-to-end; no fixes were needed.

v0.2.0-beta.51

Platform & API

Changed

  • Payment confirmation emails now link to the invoice/receipt. The "plan activated" email includes the Stripe hosted invoice link (resolved from the checkout session's invoice) and the "credits added" email includes the Stripe receipt link (resolved from the payment intent's charge) — both best-effort: if Stripe lookup fails, the email still goes out without the link. Complements Stripe's native customer receipt/invoice emails (enabled in the dashboard) without duplicating them.

v0.2.0-beta.50

Platform & API

Security

  • Secrets are now masked in every log surface. User builds routinely print env values (console.log(process.env), framework error dumps, connection-string errors) — those secrets used to land verbatim in build logs, the persisted 7-day runtime logs, and live log streams. A per-project masker (built from the project's decrypted env values via a sensitive-key/long-value heuristic, plus ad-hoc secrets like git access tokens) now replaces occurrences with •••••• at write/stream time: the deploy-log choke point (addLog), the log-collector's persisted chunks (10-min-cached masker), and both live SSE container streams. Multi-line values (PEM keys) are masked line-by-line; trivial values (production, ports…) are left alone so logs stay readable. 8 unit tests.

Added

  • Invoice history endpoint — GET /api/v1/billing/invoices lists the organization's Stripe invoices (number, date, amount, status, hosted/PDF links; last 24). Returns [] when Stripe isn't configured. Pairs with the dashboard's new Billing → Invoices section.

v0.2.0-beta.49

Platform & API

Added

  • Admin event notification emails. Set ADMIN_NOTIFY_EMAILS (comma-separated, multiple operators supported) and every significant platform event emails the list: user registration, subscription activated/canceled, payment failed, infra wallet credited, server created (managed & BYOS) / deleted / suspended for billing, project created/deleted, database created/deleted, and failed deployments — 14 instrumentation points. Delivery rides the existing BullMQ infrastructure (new admin-notify queue + worker, 3 retries with backoff) with a direct-send fallback when Redis is unset; every call site is fire-and-forget so a mail failure can never break or slow the underlying operation. Emails are a clean field-table template (HTML+text, HTML-escaped). Unset = feature off.

v0.2.0-beta.48

Platform & API

Fixed

  • CRITICAL — hourly infra billing overcharged small servers up to ~2.5×. The integer hourly price was derived with a double rounding (EUR→USD round on a sub-cent amount, then margin ceil): a server quoted $5.75/mo was actually billed 2¢/hour = $14.60/mo, draining a month of credits in ~2 weeks and then auto-suspending the server. Billing now accrues from the accurate MONTHLY price prorated over elapsed wall-clock time, carrying sub-cent remainders in millicents (new infra_billing_carry_millicents column, migration 0034) — the long-run total equals monthly/730 per hour exactly (unit-tested: 730 hourly ticks bill the monthly price ±1¢; restart-heavy schedules bill the same as regular ones). The displayed hourly price is now derived from the customer monthly with a single rounding, and the monthly-burn estimate uses the monthly price directly.
  • Restarting a suspended server no longer demands a full month's balance. start required customerPriceMonthlyCents in the wallet; it now requires 72 hours of coverage — and starting resets the billing anchor so stopped time is never billed.

Added

  • `npm run refund:infra-overcharge` — computes, per organization, the difference between what server_hourly_charge transactions actually debited and the fair monthly-rate amount (each old charge was intended to be one hour), and credits it back as an adjustment. Dry-run by default; --apply to execute.

v0.2.0-beta.47

Platform & API

Added

  • SECURITY.md — vulnerability disclosure policy (private reporting via email or GitHub's Report-a-vulnerability, 72h acknowledgement, scope notes for the deploy-isolation surface). Part of the pre-open-source hygiene pass; a repo-wide scan confirmed no secrets or personal data in tracked files.

Changed

  • README now opens with a real product screenshot (the redesigned landing hero).

v0.2.0-beta.49

Dashboard

Changed

  • Floating capsule navbar. The landing header is no longer a full-width bar: it now floats slightly inset from the top as a rounded-full capsule — hairline border, backdrop blur, soft shadow — matching the page's pill language. Verified at top, scrolled (blurring over content) and on mobile.

v0.2.0-beta.48

Dashboard

Changed

  • Marketing pages brought into the landing's design language. /about redesigned: eyebrow pill on the hero, correctly-scaled section headings (the giant lp-section-title clamp no longer leaks into subsections), black icon chips on the What-We-Do grid, values as a three-column card row with staggered reveals. Both /vs/* comparison tables gain the same Pushify-column spotlight as the homepage table. /features, /pricing, /open-source and /deploy/* inherit the earlier token refinements (pill labels, card radius/shadows, CTA polish) automatically.

v0.2.0-beta.47

Dashboard

Added

  • Product showcase — the dashboard, shown big. New full-width section right under the hero: a high-fidelity, code-built replica of the actual dashboard in a browser frame (sidebar with active nav, ⌘K search pill, stat cards with mono numerals, a recent-deployments list with Live/Building chips, and a CPU/memory metrics panel with an area chart). Pure CSS/SVG — crisp on any display, adapts to light/dark automatically, no image assets to go stale. This is the Cal.com move the page was missing: show the product, don't describe it. EN/TR i18n.

v0.2.0-beta.46

Dashboard

Changed

  • Cal.com-grade design-system refinement across the landing. Token-level polish so the whole page shifts together: eyebrow labels became bordered pill badges; cards moved to a softer 16px radius with a quiet base shadow; section vertical rhythm widened (clamp(5rem…7.5rem)); headline tracking tightened and the lead size refined for a calmer hierarchy; primary CTA gains depth (shadow + hover lift) and the ghost CTA sits on a surfaced background. Colors and fonts untouched — the same palette, rendered with more precision.

v0.2.0-beta.45

Dashboard

Changed

  • Frameworks, Marketplace, Site Builder and Security sections professionalized. Frameworks: marquee pauses on hover and the weak footer line became a bordered strip with a mono $ git push → ✓ framework auto-detected — zero config chip (EN/TR). Marketplace: six recognizable apps (Supabase, WordPress, n8n, Cal.com, Ghost, PostgreSQL) promoted to larger featured tiles with icon plates above the compact grid — a curated bento feel. Site Builder: editor mockup gains browser traffic-dots (chrome consistency with the hero) and reveals; value cards stagger in with equal heights. Security: cards switch to a denser horizontal layout — black icon chip beside the title — matching the page's monochrome language. Also removed the provider name from the How-it-works server vignette (2 vCPU · 4 GB RAM).

v0.2.0-beta.44

Dashboard

Changed

  • Homepage sections redesigned in the hero's visual language. The stats row became a single divided spec-strip (bordered card, mono tabular numerals). Each How-it-works step now carries a mini product vignette: a GitHub repo row with a main branch chip, a server row (fra1 · Hetzner · 2 vCPU) with a green status dot, and a two-line dark mini-terminal ending in ● Live at my-app.pushify.dev — 47s — the steps show the product instead of describing it. Security cards get staggered reveals and equal heights. All monochrome, derived from existing tokens; colors and fonts untouched.

v0.2.0-beta.43

Dashboard

Changed

  • Landing lower sections polished to match the new hero. "What is Pushify" pillars became icon cards with staggered reveals (matching How-it-works); the comparison table gained a spotlight on the Pushify column (tint + borders, bolder checks); the closing CTA sits on the dot-grid backdrop and now ends with the real one-command self-host install in a terminal-styled chip with a copy button (EN/TR i18n). Marketplace/Security/Site-builder cards inherit the new hover-lift automatically.

v0.2.0-beta.42

Dashboard

Changed

  • Landing redesign — motion and depth, same palette and type. The hero is now a two-column "deploy theater": a live terminal that replays a real Pushify deploy line by line (git push → framework detection → Docker build → blue-green switch → Live at your URL, with the status pill flipping to Live) on a continuous loop, layered over a dashboard card for depth, on a faint dot-grid backdrop. Site-wide scroll-reveal system (every section header + staggered card grids via a reusable <Reveal>), quiet hover-lift on all lp-cards, and a dashed pipeline connector between the How-it-works steps. All motion respects prefers-reduced-motion (terminal renders the full transcript statically); colors and fonts untouched — the new atmosphere is derived entirely from existing tokens. Verified with before/after screenshots in light, dark and mobile.

v0.2.0-beta.41

Dashboard

Changed

  • ⌘K now searches your actual resources. The command palette was a static page list; typing now also searches projects (name/slug/framework), servers (name/IP/region) and databases (name/engine) and jumps straight to the matching detail page. Entity data loads only while the palette is open and shares the app's query cache; results are grouped (Projects / Servers / Databases / Actions / Pages) and capped at 6 per group.

v0.2.0-beta.40

Dashboard

Added

  • Auto-Sleep (scale to zero) UI (pairs with backend beta.46). Project Settings gains an "Auto-Sleep" card: enable per project and set the idle window (5–1440 minutes). The Overview tab shows a sleeping/waking banner with a Wake now button when the app has been put to sleep. EN/TR i18n (sleep namespace).

v0.2.0-beta.39

Dashboard

Added

  • App Shell — web terminal into the running app container (pairs with backend beta.45). New /dashboard/projects/:id/shell page (xterm.js, same terminal chrome as the server terminal) attaching an interactive shell inside the project's container over the runner-aware SSH path; opens via the Shell button in the Logs tab. ServerTerminalView now accepts projectId for the app-shell socket alongside serverId.

v0.2.0-beta.46

Platform & API

Added

  • Scale-to-zero (auto-sleep). Opt-in per project: after sleepAfterMinutes (default 30, 5–1440) with no meaningful traffic — judged from the container's rx/tx counters in container_metrics, with a 500KB window threshold that swallows health-check chatter and a post-deploy/wake grace period — a 5-minute sweeper stops the container (SSH on server/runner, local fallback) and marks the project sleeping. Wake on request: generated nginx vhosts now include an error_page 502 = @pushify_wake fallback proxying to the new public /api/v1/wake/:slug endpoint, which CAS-claims the wake (exactly one starter under concurrent visitors), docker starts the container, and serves an auto-refreshing "Waking up…" page — genuine crashes get a branded unavailable page instead of a raw nginx 502 (fallback emitted only when API_BASE_URL is set). Also: authenticated POST /projects/:id/wake for the dashboard, health checks skip sleeping/waking apps (auto-restart would fight the sweeper), a deploy resets sleep state, and disabling auto-sleep while asleep starts the container back up. Migration 0033 adds the projects columns.

v0.2.0-beta.45

Platform & API

Added

  • Web shell into the app container. New WebSocket endpoint /ws/projects/:projectId/shell (same message protocol as the server terminal): SSHes to the server the container actually runs on — the project's assigned server or its sticky runner — resolves the blue/green container on the host and attaches an interactive docker exec (bash when the image has it, sh otherwise) over a real PTY. SSHShellSession gained an exec-with-PTY mode alongside the login shell. Owner/admin only, slug validated before command interpolation, shares the 50-session cap.