Skip to main content
Dispatches

Fibe Blog

Updates, guides, and engineering notes from the workshop.

Six Genie providers behind one interface

· 10 min read

A Genie is an AI coding agent that lives next to your code. On Fibe you spin one up, it gets its own subdomain, and you talk to it in a pane in the browser. Behind that pane is a Docker Compose project running as a sidecar on your Marquee. The interesting part is that the same machinery runs Gemini, Antigravity, Claude Code, OpenAI Codex, Cursor, and OpenCode — six providers with six different CLIs, six different credential layouts, and six different opinions about how authentication should work — and the runtime that deploys them does not know, or care, which one it just shipped.

That last sentence is the whole design. When you can add a coding agent without touching the deploy pipeline, the reconciler, or the healthcheck, adding the seventh provider stops being a project and becomes an afternoon.

The polyglot stack behind Fibe: Rails, a Rust core, a NestJS genie, and a Go SDK

· 21 min read

People assume four languages means four teams who couldn't agree, or four phases of a rewrite that never finished. Neither is true here. Fibe speaks Ruby, Rust, TypeScript, and Go on purpose, and each one is doing a job the others would do worse. The interesting part isn't the list — every grown-up system has a list. It's the rule that decides which language a given function lands in, and how boring we kept the seams between them.

This post is the honest version: what each piece is, why it exists, and the trade-offs we swallowed to get here. If you've ever stared at a monolith and wondered when to carve a piece off — and into what — this is one team's answer.

The fibe.gg/* label namespace, explained

· 15 min read

A Fibe template is a Docker Compose file with a sprinkling of labels. That's the whole trick. You don't learn a new config language — you annotate the Compose you already know, and the annotations all live under one namespace: fibe.gg/*. Set fibe.gg/port: 3000 on a service and it gets a public HTTPS URL. Set fibe.gg/production: "false" and Fibe mounts your repo into the container so edits show up live. Twenty labels, four jobs.

The catch is that the namespace is strict. An unknown fibe.gg/* key isn't ignored — it's a hard parse error that names the offending service. That's deliberate (a typo'd fibe.gg/prot should fail loudly, not silently route nothing), but it means you want the exact spellings, the exact value rules, and the handful of "label X requires label Y" rules in front of you. This post is that lookup table, grouped the way you actually reach for them.

/opt/fibe: the host filesystem and running compose over SSH

· 10 min read

Here is the mental model most people start with: Fibe checks out your repo, runs docker compose up from the repo root, and that's your environment. Clean picture. Wrong in every load-bearing detail.

There is no repo root Fibe cds into, no checkout you pushed. The control plane generates a compose file from your Template, rsyncs it to a precise path on your Marquee, and runs docker compose -p <project> -f compose.yml up over SSH — explicit -p and -f, never "whatever's in this directory." This post walks that machinery: the /opt/fibe layout, the generate → rsync → up pipeline, and the marker that makes cleanup safe.

Coordinating reconcilers with Redis token locks

· 10 min read

Every 60 seconds, Fibe's reconciler — we call it Playguard — wakes up and walks each Marquee, asking the same question: is reality what the database says it should be? It pulls git branches, refreshes credentials, pulls images, detects drift on Playgrounds, recovers stuck ones, enforces expirations, sweeps orphaned containers. All of that happens over SSH, against a real Docker daemon, on a host we don't own.

Here's the nightmare we work to avoid: two of those runs touching the same Marquee at the same time. One job tearing down a container while another rebuilds it. Two docker compose up calls fighting over the same project. A drift "fix" applied on top of a half-finished "fix." Over SSH, against a stateful daemon, concurrency isn't a performance problem — it's a correctness problem. So Playguard runs each Marquee under a lock. The interesting part isn't that we take a lock; it's that we keep proving we still hold it.

Welcome to fibe.gg

· One min read

We're excited to launch the Fibe blog. This is where we'll share product updates, engineering insights, and guides on getting the most out of Fibe environments.

Keeping a Rails monolith modular with Packwerk

· 10 min read

Every few months someone asks when Fibe is going to "break the monolith into services." It's a reasonable question. We provision Docker hosts, route TLS, run AI coding agents, charge money, and reconcile container state every sixty seconds — all in one Rails app that ships in one deploy. On paper, the thing the microservices talks warn you about.

It isn't, and the reason is boring in the best way: the monolith is split into ten-plus enforced domains whose boundaries are checked in CI, so a call from the billing domain into the provisioning domain's internals fails the build like a syntax error would. We get the part of microservices that matters — domains that can't secretly reach into each other — and skip the part that hurts: a network between every method call.

Templates, Playspecs, and the Bazaar: how environments are described

· 9 min read

Every Fibe environment starts as a description before it is ever a running thing. Long before a container exists on a Marquee, before Traefik knows a subdomain, before the wallet is even checked — there is a document that says what this environment is. That document is a Template, and the way Fibe treats it tells you a lot about the platform's whole personality: declarative, versioned, shareable, and unforgiving about typos.

This post is about the authoring layer — what you touch when you decide what an environment should be, not how it gets healed at 3am. We'll walk from a Template to a Playspec to the Bazaar, with stops at variables, the fibe.gg/* config keys, and why unknown keys and unresolved ${...} placeholders hard-fail.

Props: one Git binding feeding every dynamic service

· 10 min read

Early on, Fibe passed repository URLs around like loose change. A Playground needed source, so it took a URL. An image build took a URL — and a token, and a branch, and a guess about which provider this was and how to authenticate. Every place that touched a Git repo re-derived the same three or four facts, and every place got to be wrong in its own way.

So we stopped. A repo became a resource you bind once and reuse: a Prop. Clone it for a Playground, build an image from it, fire a headless Trick when it changes — all from the same binding, with auth resolved in one place. Here's why that's a real model, not just a wrapper around a string.

Why we carved a Rust core out of our Rails app

· 10 min read

Fibe has been a Rails monolith since the first commit, and after a couple of years running a Docker-based dev-environment platform on it, we still think that was the right call. So when a small Rust workspace showed up in the repo, the obvious question — including from across the team — was some flavor of "oh no, are we rewriting in Rust?"

No. We extracted a very specific, very boring slice of the codebase into Rust, behind a rule we can state in one sentence. This post is that rule, why it works, and the genuinely annoying parts.