Skip to main content
Dispatches

Fibe Blog

Updates, guides, and engineering notes from the workshop.

Teams on Fibe: sharing Marquees and resources without stepping on each other

· 13 min read

You bought the beefy box. You funded the Marquee. And now three teammates are pinging you in Slack asking why they can't launch the staging stack on it. The honest answer for a long time was "you can't, it's mine" — and that's a silly place for a tool that runs Docker hosts to be.

Teams fix that. The model is deliberately small: a Team lets you share one thing — a Marquee — with other people, and the people you share it with launch on it as if it were their own. No team wallet, no read-only tier, no labyrinth of per-resource ACLs. This guide walks you through creating a team, inviting people, picking roles, and sharing a Marquee — and, just as importantly, what stays private and what disappears the moment access ends. The boundaries are where people get surprised, so we'll spend real time there.

The Terraform webhook state engine: generations and stale rejection

· 10 min read

A Player buys a tutorial plan. Somewhere out in GitHub Actions, a Terraform workflow spins up a Scaleway VM, wires up Cloudflare DNS, registers an acme-dns account, and reports back over HTTP. We don't poll it, and we don't hold a connection open for the eight minutes it takes — we wait for it to call us, and when it does, we have to decide in a few milliseconds whether to believe it.

That last part is the whole problem. The workflow is out of band, eventually consistent, and occasionally late. A deploy you kicked off ten minutes ago might still be running when you've given up and started a fresh one — and when that slow old workflow finally phones home, trusting it clobbers the newer environment with its stale view of the world. This post is about the machinery — generations, a drift guard, a two-confirmation null — that lets us listen to an untrusted, asynchronous system without getting burned.

Static or dynamic? Choosing image-only vs source-backed services

· 15 min read

Every service in a Fibe template is one of exactly two kinds, and the difference decides everything downstream: whether Fibe clones a repo, whether it runs a build, whether you can edit a file and watch the page reload, and whether a typo in a label hard-fails your launch. The kinds are static (runs a published image) and dynamic (source-backed — Fibe clones, builds, or mounts a Git repo). Most templates are a mix: a dynamic app service in front of a couple of static dependencies.

Here's the part people get wrong: the kind isn't something you declare. There is no fibe.gg/type label. Fibe derives it from the labels you set. Get the signals right and the classifier does the rest; get them subtly wrong and you'll see errors like Service 'web' has a build directive but lacks a fibe.gg/repo_url label. This guide walks the dividing line, then shows you how to wire each kind with real YAML you can copy.

Anatomy of a Playground: the eight-step creation pipeline

· 21 min read

You click "create," and a few seconds later there's a URL that serves your app. In between, a surprising amount happens — most of it on a machine you've never logged into. A Playground on Fibe isn't a container we start. It's a small, ordered ritual: claim a database row, clone source onto a remote host, merge registry credentials, build an image, render a compose file from a template, ship it over SSH, and only then run docker compose up — on the Marquee, not on us.

This post walks that ritual end to end. Eight steps, nine statuses, one set of legal transitions, and a fair amount of paranoia about what happens when a step fails halfway through. If you've ever wondered what "in progress" actually means while the spinner spins, this is the tour.

The validation pipeline: which layer catches which mistake

· 15 min read

Here is the thing nobody tells you about template errors: most of them are easy once you know who is complaining. Fibe doesn't validate your template in one big pass. It runs a series of layers, each one authoritative for a narrow scope, each one with its own vocabulary. A YAML parser and a launch API say "this is wrong" in completely different dialects. If you can read an error and instantly think "ah, that's the schema layer" or "that's the compiler, not the schema," you stop guessing and start fixing.

This guide walks the layers in order, shows you the exact class of mistake each one catches, and gives you the smallest change that makes it green. The theme throughout: Fibe fails fast and fails specifically, and the earlier a layer catches your problem, the cheaper it is to fix.

Subdomains, ports, and collisions: keeping environments uniquely addressable

· 9 min read

A Marquee is one Docker host. On it, a Player might run half a dozen Playgrounds at once — a couple of long-lived dev environments, a headless Trick chewing through a job, an AgentChat sidecar where a Genie is editing code. They share the same daemon, Traefik instance, range of host ports, and wildcard domain. And every one needs a URL that goes to exactly one place.

That's the whole problem: shared host, distinct addresses. Get it wrong and two environments fight over app.your-host or both grab host port 8080 — Traefik routes arbitrarily, or Docker refuses the second container, and the Player gets a confusing failure deep in the creation pipeline. Our answer is boring on purpose: we refuse to save an environment that would collide, before it touches the host. Validate at write time, routing stays dumb.

Tricks: headless one-shot Playgrounds for automation

· 9 min read

Most platforms ship a development environment and a CI system as two separate products. They drift. What worked in your dev container fails in CI for reasons nobody can reproduce, and you lose an afternoon discovering the CI runner is on a different base image, network, everything.

We didn't want two runtimes. A Trick is a Playground with one flag flipped — job mode — running the exact same environment engine, on the same Marquee, from the same Template. It has no UI to attach to and a defined notion of done: it runs, it exits, it records a result. If your tests pass in a Playground, they pass in a Trick — there's no second runtime to drift away from.

Gradual typing at scale: Sorbet in a big Rails app

· 9 min read

Fibe runs Docker development environments for people, and the brain of that is a Rails modular monolith. It decides when a Playground is healthy, when to tear one down, when a Wallet has run dry, and when a state transition is illegal and should be refused. That code is dynamic Ruby, and it is load-bearing in the most literal sense: get a state transition wrong and you destroy someone's running environment.

So we type it — but not all of it. Typing every line of a Rails app is a great way to spend a quarter and ship nothing. We type the parts where a wrong shape becomes a wrong outcome, and leave the rest dynamic. This post is about where that line is.

The onboarding wizard: from signup to your first environment

· 9 min read

Fibe has a vocabulary problem, and we knew it the day we shipped. A new player signs up and within thirty seconds must hold a half-dozen made-up nouns in their head: Marquees, Props, Playspecs, Playgrounds, Tricks, Crumbs, the Bridge. None mean what they mean anywhere else — you cannot Google "what is a Marquee" and get our answer. The first time someone lands in the app, the dashboard is empty and every label is a word they have never seen, on a concept they do not have.

You can solve that with documentation. We tried. Nobody reads it. So we built an onboarding wizard that walks a brand-new player from a blank account to one real, running environment — teaching the nouns by doing, not reading, and never sending them to a manual.

One Subscription, three payment providers

· 9 min read

Most of our billing code does not know which payment provider charged the card. That is on purpose, and one of our better early decisions.

Fibe takes money through three processors — Stripe, Creem, and Paddle — but the rest of the platform sees exactly one thing: a subscription record and a paid order. The provider name is just a stored string. Everything downstream — minting Mana, provisioning a tutorial Marquee, converting to Sparks, the daily funding sweep that keeps your environment alive — reads from that neutral layer and never branches on "is this Stripe or Paddle." Here's how, and why it earns its keep.