Skip to content

feat(fly): add Fly.io cloud substrate (--on fly)#5

Merged
snowmead merged 4 commits into
mainfrom
feat/flyio-substrate
Jun 17, 2026
Merged

feat(fly): add Fly.io cloud substrate (--on fly)#5
snowmead merged 4 commits into
mainfrom
feat/flyio-substrate

Conversation

@snowmead

Copy link
Copy Markdown
Owner

Summary

Adds Fly.io as a --on fly cloud substrate — the next hosting target in the suite after render and vercel. v0 is image-only: a service declares a prebuilt [services.X.fly].image and the substrate deploys it as a Fly machine.

How it works

  • Stripe Projects provisions flyio/app and returns a Stripe-managed, app-scoped DEPLOY_TOKEN (pinned via mise run discover flyio/app). The Fly Machines REST API (hand-written fly_api.rs — the served spec is Swagger 2.0 and we use ~6 endpoints, vendored under specs/ for reference) allocates the app's public IPs, creates the machine, and polls it to started; then a health gate on https://{app}.fly.dev.
  • The deploy token is ephemeral, so observe/destroy key off the Stripe resource registration (resource_registered / remove_resource) — the Fly API is touched only at deploy time.
  • No managed datastore in v0 (flyio/mpg is a separate catalog integration; [datastores.*] is rejected).
  • One-row registration across every seam: workspace member, binary dep, substrates.rs (row + builder + global codes-uniqueness test), --on help, SubstrateRequired remediation, integrations KNOWN.

Validation

  • ✅ 219 hermetic workspace tests; clippy / rustfmt / taplo clean
  • Live smoke green end-to-end (mise run smoke-fly): provision → deploy-token → IPs → machine started → HTTPS health at *.fly.dev → verified teardown

Notes / follow-ups

  • Fly-org billing is a one-time per-account step: create_machine 422s until a card is on the Stripe-managed Fly org (stripe projects open flyio → Fly dashboard). No Stripe Projects CLI command propagates it today — a Stripe↔Fly integration gap worth reporting upstream.
  • Partial-provision orphan: a mid-start failure (Stripe add succeeds, deploy fails) leaves the flyio/app resource un-reaped until a later successful up→down (same shape as render). A Codex review of this exact case is in flight; any fix folds in here.
  • Docs: README, ARCHITECTURE §4c, docs/SCHEMA.md, and a new "One-time setup: initialize + link the provider" section in ADDING-A-PROVIDER.md (the stripe projects init + link ritual, run from the fixture dir).

🤖 Generated with Claude Code

snowmead and others added 3 commits June 17, 2026 10:56
The next hosting target in the substrate suite, after render and vercel.
v0 is image-only: a service declares a prebuilt `[services.X.fly].image`
and the substrate deploys it as a Fly machine.

- crates/stackless-fly impl Substrate: Stripe Projects provisions
  `flyio/app` and returns a Stripe-managed, app-scoped DEPLOY_TOKEN (pinned
  by `mise run discover flyio/app`); the Fly Machines REST API allocates the
  app's public IPs, creates the machine, and polls it to `started`;
  health-gates on `{app}.fly.dev`. observe/destroy key off the Stripe
  resource registration (the token is ephemeral), so the Fly API is touched
  only at deploy time. No managed datastore in v0.
- fly_api.rs is hand-written reqwest/serde, not progenitor: the served Fly
  Machines spec is Swagger 2.0 and we use ~6 endpoints. Spec vendored under
  specs/ for reference.
- Registered across the one-row seams: workspace member, binary dep,
  substrates.rs (row + builder + global codes-uniqueness test), `--on` help,
  SubstrateRequired remediation, integrations KNOWN.
- Tests: hermetic catalog-gap + config + Stripe-scripted observe/destroy +
  mock-API machine deploy (19). Live-validated end-to-end via
  `mise run smoke-fly` (provision -> deploy -> HTTPS health -> verified
  teardown).
- Docs: README, ARCHITECTURE 4c, docs/SCHEMA.md, and a new "One-time setup:
  initialize + link the provider" section in ADDING-A-PROVIDER.md covering
  the `stripe projects init` + `link` ritual (run from the fixture dir).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A re-run after `create_machine` succeeded but `wait_for_started` failed would
create a duplicate (billing) machine — `create_machine` is not idempotent.
`start_service` now checks for an existing machine by name (`find_machine`) and
reuses it before creating. Surfaced by an adversarial self-review of the resume
path; re-validated by the live smoke.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The second hosting target in the suite, after Fly. v0 is static-upload: a
service's source files (under [services.X.netlify].root or the repo root) are
deployed via Netlify's file-digest API.

- crates/stackless-netlify impl Substrate: Stripe Projects provisions
  `netlify/project` (free) and returns a Stripe-managed token + site id (pinned
  by `mise run discover netlify/project`; the token surfaces on a refreshed env
  read). The substrate clones the pinned ref and runs the file-digest deploy
  (SHA1 per file, PUT only the `required` files, poll to `ready`),
  health-gating on the deploy's ssl_url. observe/destroy key off the Stripe
  resource registration (the token is ephemeral).
- netlify_api.rs is hand-written reqwest/serde (Swagger-2.0 source, ~5
  endpoints).
- Registered across the one-row seams: workspace member, binary dep,
  substrates.rs (row + builder + global codes-uniqueness test), --on help,
  SubstrateRequired remediation, integrations KNOWN.
- Tests: hermetic catalog-gap + config + Stripe-scripted observe/destroy +
  mock-API file-digest deploy (15). Live-validated end-to-end via
  `mise run smoke-netlify` (provision -> upload deploy -> HTTPS health ->
  verified teardown).
- Docs: README, ARCHITECTURE 4d, docs/SCHEMA.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(netlify): add Netlify cloud substrate (--on netlify)
@snowmead snowmead merged commit 72f4347 into main Jun 17, 2026
11 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.

1 participant