Skip to content

Add per-second pricing with Stripe metered billing + free tier#89

Merged
motatoes merged 282 commits into
mainfrom
feat/stripe-billing
Mar 27, 2026
Merged

Add per-second pricing with Stripe metered billing + free tier#89
motatoes merged 282 commits into
mainfrom
feat/stripe-billing

Conversation

@motatoes

Copy link
Copy Markdown
Contributor

Summary

  • Free tier: 1 concurrent sandbox, 4GB/1vCPU only, no card required
  • Pro tier: All tiers (4GB–64GB), 5 concurrent sandboxes, per-second metered billing via Stripe
  • Upgrade flow: Stripe Checkout (setup mode) → creates subscription with 5 Billing Meter prices → applies $30 promotional credit → plan set to "pro"
  • Usage reporting: Background worker reports sandbox compute seconds to Stripe every 5 min via Billing Meter Events
  • Invoicing: Stripe generates monthly invoices automatically, $30 credit applied to first invoice(s)
  • Dashboard: Billing page shows plan status, current month usage breakdown, pricing table, invoice history, and spending cap settings

Pricing (per second)

Memory vCPU $/second
4 GB 1 $0.00003240740741
8 GB 2 $0.000150462963
16 GB 4 $0.0008101851852
32 GB 8 $0.005787037037
64 GB 16 $0.0162037037

New files

  • internal/billing/stripe.go — Stripe client (meters, prices, subscriptions, checkout, webhooks)
  • internal/billing/pricing.go — Tier pricing constants
  • internal/billing/usage_reporter.go — Background usage reporter
  • internal/api/dashboard_billing.go — Billing API endpoints + Stripe webhook handler
  • internal/db/migrations/017_stripe_billing.up.sql — Billing columns + subscription items table
  • web/src/pages/Billing.tsx — Billing dashboard page

Env vars required for Stripe

STRIPE_SECRET_KEY=sk_...
STRIPE_WEBHOOK_SECRET=whsec_...
STRIPE_SUCCESS_URL=https://yourdomain.com/billing?success=true
STRIPE_CANCEL_URL=https://yourdomain.com/billing?cancelled=true

Stripe products, meters, and prices are created automatically on first server startup via EnsureProducts().

Test plan

  • Start server without Stripe env vars → billing features disabled, free tier enforced
  • Start with Stripe test keys → meters + prices created in Stripe Dashboard
  • Free tier: create 4GB sandbox ✓, try 8GB sandbox → 402 blocked
  • Click "Upgrade to Pro" → Stripe Checkout → add test card (4242...) → webhook upgrades plan
  • Pro tier: create any size sandbox → works
  • Insert scale events → usage reporter sends meter events to Stripe
  • Billing page shows usage breakdown + $30 credit
  • Stripe Dashboard → Billing → Meters shows recorded events

🤖 Generated with Claude Code

motatoes and others added 30 commits February 26, 2026 16:23
… (pip)

- Rename TypeScript package from opensandbox to @opencomputer/sdk
- Rename Python package from opensandbox-sdk to opencomputer
- Rename Python module directory from opensandbox/ to opencomputer/
- Update all imports in docs, examples, and READMEs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update both SDKs, docs, examples, and test scripts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rebrand from opensandbox.ai to opencomputer.dev
Preview URLs are now created explicitly via the SDK (createPreviewURL/
getPreviewURL/deletePreviewURL) rather than auto-assigned on sandbox
creation. Each preview URL registers a Cloudflare custom hostname
(e.g. sb-xxx.openlovable.cc) for per-sandbox routing through the org's
verified custom domain.

Key changes:
- DB migrations for org custom domains and sandbox_preview_urls table
- Cloudflare client for custom hostname CRUD and domain verification
- API endpoints: POST/GET/DELETE /api/sandboxes/:id/preview
- Dashboard UI for custom domain setup with DNS verification flow
- Proxy routing: both SandboxProxy and ControlPlaneProxy resolve
  custom hostnames via DB lookup fallback
- ControlPlaneProxy rewrites Host header to {sandboxID}.{baseDomain}
  so workers can match custom domain requests
- Auto-cleanup of preview URLs on sandbox kill
- TypeScript and Python SDK methods for preview URL management

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove automatic domain routing from sandboxes. Preview URLs are now the
only way to get HTTP access, each targeting a specific port. Hostname
format {sandboxID}-p{port}.{baseDomain} lets the proxy parse sandbox ID
and port directly — zero DB lookups on the hot path.

- Add DB migration for port column + unique constraint
- Add ContainerAddr to Manager interface (Podman + Firecracker)
- Rewrite proxy to parse hostname instead of DB lookup
- Add friendly HTML placeholder page for upstream unavailable (auto-refresh)
- Update API handlers: create/list/delete preview URLs by port
- Update dashboard, SDKs (TypeScript + Python), and types

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When an org has a custom domain configured, the dashboard now shows
preview URLs using the custom domain (e.g. sb-xxx-p3000.openlovable.cc)
as the primary link, with a collapsible "Internal URLs" section showing
the base domain URLs underneath.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add on-demand preview URLs with custom domains
- deploy-server: builds Go server + web dashboard, deploys to control plane via SSH
- deploy-worker: builds worker + agent binaries (arm64), deploys to worker via SSH
- publish-ts-sdk: publishes @opencomputer/sdk to npm on ts-sdk-v* tags
- publish-python-sdk: publishes opencomputer-sdk to PyPI on py-sdk-v* tags via trusted publisher

All server IPs stored as GitHub secrets (SERVER_IP, WORKER_IP).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add CI/CD pipelines for server, worker, and SDK publishing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
npm provenance verification requires repository.url to match the actual
GitHub repo (diggerhq/opencomputer, not the old opensandbox name).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Publishes automatically when sdks/typescript/ or sdks/python/ change on
main (i.e. after PR merge). Skips publish if the version already exists
on the registry to avoid failures on non-version-bump changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Switch SDK publish triggers from tags to push-to-main with path filters
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Updated project name and description, added documentation link.
- createPreviewURL returns existing URL (200) instead of 409 when one already exists for the port
- Add optional `domain` field to creation request; validates it matches the org's verified custom domain and registers with Cloudflare
- Include `customHostname` in create/list responses when the org has a custom domain
- Update TypeScript and Python SDKs with `domain` parameter and `customHostname` field
- Update test-preview-urls.ts to cover idempotent creation and custom domain flows

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…custom-domains

Make preview URL creation idempotent and add custom domain support
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…sions

Switches from bundler to nodenext moduleResolution so emitted JS includes
explicit .js extensions, fixing module resolution errors for Node.js ESM
consumers. Bumps TypeScript SDK to 0.5.7.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a new "default" Dockerfile that bundles Python 3, Node.js 20 LTS,
build-essential, git, and common utilities into a single rootfs image.
Updates build-rootfs.sh to support the new "default" target and adds a
migration to seed the template in the database.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add default template image with Python 3, Node.js 20, and build tools
The migration file was added but not registered in the hardcoded
migrations slice, so it was never applied on server startup.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Update all docs to reference "default" template instead of "base"
- Add TypeScript and Python test scripts for default template verification
- Add deploy/ec2/deploy-rootfs.sh for pipeline-friendly rootfs image builds
- Add guides section with build-a-lovable-clone guide

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add coming-soon items: custom base images via SSH snapshots,
private link sharing, and no-private-sharing limitation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
breardon2011 and others added 6 commits March 25, 2026 11:03
Implements usage-based billing via Stripe Billing Meters:
- Free tier: 1 sandbox, 4GB/1vCPU only, no card required
- Pro tier: all tiers, 5 concurrent, per-second billing via Stripe
- Upgrade flow: Stripe Checkout → subscription with 5 metered prices → $30 credit
- Usage reporter sends meter events to Stripe every 5 min
- Stripe handles invoicing, receipts, and payment collection
- Billing dashboard page with plan status, usage breakdown, pricing table, invoices
- Free tier enforcement in sandbox creation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Mar 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
opensandbox Ready Ready Preview, Comment Mar 27, 2026 1:37am

Request Review

- Update file limit docs to reflect the 2GB per-file transfer cap
- Bump TypeScript SDK to 0.5.13
- Bump Python SDK to 0.4.10 / 0.5.2

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
breardon2011 and others added 3 commits March 26, 2026 15:52
* fixes

* fix tests

* virtio-mem fixes

* add limit for streaming paths

* drop writeStream/readStream

* corruption testing
Resolves conflicts in store.go (Org struct + orgColumns + scanOrg),
router.go (dashboard routes), and client.ts (types + API functions).

Migration numbering: 16=workos, 17=sandbox_usage, 18=secret_allowed_hosts, 19=stripe_billing.
Removed duplicate credit_balance_cents from stripe migration (already in 016_orgs_workos).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…et_allowed_hosts)

These are pre-existing unregistered migration files — they should be handled in a separate PR.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment thread server Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

binary

@breardon2011 breardon2011 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approve - should take out binary though

These migration files existed but were never registered in the migration list.
Both use IF NOT EXISTS/IF EXISTS so they're safe to run on databases where
the tables already exist (production).

Migration order: 17=sandbox_usage, 18=secret_allowed_hosts, 19=stripe_billing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@breardon2011 breardon2011 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approve

# Conflicts:
#	.gitignore
#	cmd/server/main.go
#	docs/api-reference/files/read.mdx
#	docs/api-reference/files/write.mdx
#	docs/reference/python-sdk/filesystem.mdx
#	docs/reference/typescript-sdk/filesystem.mdx
#	docs/sandboxes/signed-urls.mdx
#	docs/sandboxes/working-with-files.mdx
#	go.mod
#	internal/api/router.go
#	internal/config/config.go
#	internal/db/migrations/015_secret_allowed_hosts.up.sql
#	internal/db/store.go
#	internal/db/usage.go
#	sdks/python/opencomputer/__init__.py
#	sdks/python/pyproject.toml
#	sdks/typescript/package.json
#	web/src/App.tsx
#	web/src/api/client.ts
#	web/src/components/Layout.tsx
#	web/vite.config.ts
@motatoes motatoes merged commit 7deca6c into main Mar 27, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants