# Fibe — user guide & skills
Fibe runs your projects in real Docker environments connected to your compute hosts and Git repositories. The guide covers Marquees, Props, Templates, Playgrounds, Tricks, Genies, and a full reference library of authoring skills.
https://whats.fibe.gg
---
# Get started
## Welcome to Fibe
> https://whats.fibe.gg/intro/
Fibe runs Docker environments on hosts you control, fed from Git, steered from a browser. Launch in seconds, share a URL, attach an AI assistant, stop or extend when done.
## The shortest path
1. Add a [Marquee](/concepts/marquees/). A host that runs containers.
2. Connect a [Prop](/concepts/props/). A Git repo.
3. Pick or write a [Template](/concepts/playspecs/#templates). A Compose file plus a few Fibe labels.
4. Launch a [Playground](/concepts/playgrounds/) from a [Playspec](/concepts/playspecs/). Open the URL.
## Two shapes of work
| | What it is | When |
| --- | --- | --- |
| **[Playground](/concepts/playgrounds/)** | Long-running environment. URLs, logs, terminal. | Web apps, dashboards, dev servers. |
| **[Trick](/concepts/tricks/)** | One-shot run. Records pass/fail, cleans up. | Tests, migrations, backups, cron, CI. |
If the task should finish, use a Trick. If it should stay up, use a Playground.
## Bring a Genie
A [Genie](/concepts/agents/) is a configured AI assistant. Run one standalone or attach it to a Playground. It reads logs, edits source, runs commands, commits.
Keep many. One per job — refactoring, tests, docs. Each holds many conversations.
## What's next
- **New here?** [Marquees](/concepts/marquees/) → [Props](/concepts/props/) → [Templates](/concepts/playspecs/#templates) → [Playspecs](/concepts/playspecs/) → [Playgrounds](/concepts/playgrounds/).
- **Authoring a Template?** [Compose → Fibe](/authoring/compose-to-fibe/).
- **Driving from a script or CI?** [Fibe SDK](/sdk/intro/) ships the `fibe` CLI and a Go library.
- **Wiring an AI agent that should know Fibe?** [`llms.txt`](https://whats.fibe.gg/llms.txt), [`llm-skills.txt`](https://whats.fibe.gg/llm-skills.txt), [reference library](/reference/intro/). For agents that should act, run the [MCP server](/sdk/mcp-server/).
---
# Concepts
## Agents
> https://whats.fibe.gg/concepts/agents/
A **Genie** is a configured AI assistant. Keep many — one per job. Each holds many parallel conversations.
Two ways to use one:
- **Standalone chat** — Fibe starts a chat with its own URL on a chosen Marquee.
- **Inside a Playground** — open the Genie as a side panel. It reads logs, runs commands, edits source.
## Configure a Genie
- **Provider** — Gemini, Antigravity, Claude Code, OpenAI Codex, Cursor, OpenCode.
- **Credentials** — provider-specific (OAuth, device code, pasted bundle, API key). Stored securely.
- **System prompt** — shapes behavior. E.g. "careful refactoring assistant; prefer minimal diffs; explain reasoning".
- **Custom environment values** — env vars the Genie process sees.
- **Model options** — context window, temperature, provider-specific options.
- **Custom tool servers** — additional MCP servers.
- **Mounted files** — docs, prompts, fixtures, scripts in the working tree on every run.
- **Post-init script** — runs once on environment setup. Use for `npm install`, dependency setup, anything pre-work.
- **Agent password** (optional) — passphrase to make the Genie's URL private.
## Settings cascade
Resolution order (most specific to most generic):
1. This Genie.
2. Your per-provider account default.
3. Your general account default.
4. Platform defaults.
5. Built-in defaults.
Hash-shaped settings (custom env, tool toggles) **merge across all levels** instead of replacing. Change "my default Claude model" once on the account; every Claude-based Genie picks it up.
## Standalone chat
Pick an authenticated Genie and a Marquee. Fibe starts a chat with a protected URL. You can:
- Extend it (push expiration out).
- Stop it (preserves history; restart later).
- Let it expire.
No Playground attached. Useful for ideation and docs search.
Starting, restarting, messaging, interrupting, reading live state from, stopping, or cleaning up a Genie runtime requires the selected Marquee to be funded. Unpaid Marquees fail with `MARQUEE_NOT_FUNDED`.
## Inside a Playground
Open a Genie as a side panel. It gets the run's context — logs, terminal, source, environment — and can:
- Debug failing services.
- Edit source.
- Run terminal commands.
- Generate artefacts (reports, diffs, mockups).
- Commit changes via in-browser git.
- Stay open while you work in other panes.
Switch Genies inside a Playground anytime.
## Conversations
Every chat is a **Conversation**: messages, replies, related activity stored together. A Genie holds many Conversations in parallel. Resume any later.
The special **Inbox** conversation collects general activity outside any thread — notifications, pokes, broadcasts.
## Pokes — scheduled prompts
A **Poke** sends a prompt to a Conversation on a recurring schedule. Useful for:
- Morning summary of new commits.
- Hourly deploy-log check.
- Weekly roadmap draft from open issues.
Each Poke has:
- A **cron schedule** (5-field POSIX).
- A **prompt body**.
- One **target Conversation** (not the Inbox — Pokes refuse it at validation).
Minimum interval: **five minutes**. Pause/resume from the Genie's settings. Deleting the target Conversation auto-pauses the Poke.
Scheduled Pokes claim and reschedule normally, but delivery to a Genie is blocked when the backing Marquee is unpaid.
## Notifications on activity
When a Genie sends a message:
- **In-app notification** surfaces immediately.
- Entry in the **FAB** (floating-action-button area).
- **Browser web-push** if enabled.
Audit-log entries don't notify. See [Audit log](/advanced/audit-log/).
## Genie example
```yaml
# Conceptual — set in the UI, not YAML
name: Refactorer
provider: Claude Code
system_prompt: |
Careful refactoring assistant. Prefer the smallest possible diff. Run
the relevant tests before claiming a change is done. Quote the test
output back to me.
mounted_files:
- ARCHITECTURE.md
- STYLE_GUIDE.md
post_init: npm install --silent
custom_env:
NODE_ENV: test
```
Opened in a Playground, the Genie has the docs, the env vars, and a clean install before you say a word.
## Build in Public
Opt selected Genies into your public profile. Visitors browse what you're working on without a Fibe account.
### Per-Genie toggle
Set per Genie, not per account. Flip the toggle in a Genie's settings; that Genie appears on your public profile. Other Genies stay private.
Use cases:
- Public main side project, private client work.
- Experimental Genie kept private until interesting.
- Public-facing demo Genie alongside private day-to-day Genies.
### Feature a Playground
With the toggle on, pick one of your Playgrounds to feature — typically a live demo. Visitors land on the Genie's page and see that Playground.
Rules:
- Only Playgrounds you can access are eligible.
- Turning Build in Public off clears the featured Playground.
- Featuring does **not** auto-publish the Playground. Public visibility of the Playground is separate.
### What visitors see
Public profile lists your build-in-public Genies. Visitors browse them. They can't sign in as you or modify anything. Account-level data (settings, Wallet, other Genies) stays private.
Each public Genie page shows:
- Name and short description.
- Featured Playground (if set) with its public URL.
- Timeline of public activity — public artefacts and public mutters.
Conversations and chat history are private. Publish parts explicitly as artefacts to expose them.
### Typical setup
1. Create a Genie tuned to your project.
2. Run a dev Playground with a public URL.
3. Feature the Playground on the Genie.
4. Capture milestones as artefacts marked public.
5. Share your public profile URL.
## Artefacts, mutters, feedback
The trail your work leaves behind.
### Artefact
A generated output worth keeping — report, screenshot, mockup, CSV, config snippet, interactive preview. Attaches to the Playground or Genie that produced it.
Use when:
- A Genie generates a useful file.
- A Playground produces a build output (logs, report, diagram).
- You want to mark a moment.
Mark an artefact public to surface it on a Build-in-Public profile.
### Mutter
A short progress, evidence, or issue note. Capture a "here's what I tried" or "here's where it broke", with screenshots optional.
Mutters describe **what happened**, not how. Good:
- "Migration on a fresh DB ran in 12s."
- "Healthcheck flapping. `start_period` is probably too short."
- "Pushed v2 of the auth flow. Preview at <url>."
Bad:
- "ERROR: connection refused." Paste the log into an artefact instead.
- "Refactored to use new ORM." Vague — what changed, why, what's left?
### Feedback
Rate or review a Genie's output. Feeds back into how you judge an assistant for a given task.
Use when:
- A response was particularly useful or particularly off.
- You want to remember which Genie was good at which task.
- You're evaluating providers or system prompts.
### Activity timelines
Artefacts, mutters, feedback, plus higher-level events (Genie sent a message, Trick failed, Playground rolled out) roll up into per-Playground and per-Genie timelines. Chronological order.
With Build in Public on, the public-marked subset appears on your public profile.
## FAQ
How many Genies can I have?
No hard cap. Each Genie's settings page shows current spend so you can monitor.
Two Genies share a Conversation?
No. Each Conversation belongs to one Genie. Switch Genies (new Conversation) or copy context across.
Does a Genie see my code?
In a Playground: yes — files mounted there. Standalone chat: only what you give in prompts plus mounted files. Mounted files don't leak across Genies.
Pokes and the Wallet?
Each Poke run costs the underlying model call. Poke settings show recent runs and their cost.
Can visitors chat with my public Genie?
No. Conversations belong to you. Visitors only read the profile and public artefacts.
Turning Build in Public off?
The Genie disappears from the public profile. Old URLs return 404. Re-enable to restore.
Profile public without opt-in Genies?
The profile URL exists by default. Without opted-in Genies, it's essentially empty.
Artefacts stored forever?
Until you delete them. Artefacts attached to a destroyed Playground move to the account-level artefact list.
Artefact vs file in a Prop?
A **Prop file** is source code in git. An **artefact** is a produced output — result of running something, snapshot of a state.
## Related
- [Playgrounds](/concepts/playgrounds/) — where Genies attach.
- [Run the MCP server](/sdk/mcp-server/) — Genies (and external agents) call Fibe directly. Conversation tools come from this server.
- [Advanced → Agent Defaults](/advanced/agent-defaults/) — account-wide defaults for new Genies.
- Reference: [`fibe-agents-and-automation`](/reference/fibe-agents-and-automation/).
## Bazaar
> https://whats.fibe.gg/concepts/bazaar/
The **Bazaar** is the public marketplace for [Templates](/concepts/playspecs/#templates). Browse what other Players have published. Fork, launch, or both.
## What's in the Bazaar
Any Template version a Player published with publish-ready metadata (description, category, sensible defaults). The Bazaar listing is the view filter — Templates aren't a separate codebase, the same Templates power your private launches and the public marketplace.
## Browse
The Bazaar page lets you:
- Search by name, description, or keyword.
- Filter by category (CI, web app, database, dev environment, etc.).
- Sort by recency, popularity, or last update.
- Open a Template's detail page for the description, variables it asks for, screenshots, and version history.
## Launch from the Bazaar
Pick a Template, click Launch:
1. Fibe creates a fresh Playspec bound to the Template's latest version.
2. Fill in variables the Template asks for.
3. Pick a target Marquee.
4. Launch.
The result is a normal [Playground](/concepts/playgrounds/) you fully own. The original Template stays where it was — your Playspec carries a reference to the version you launched.
## Fork from the Bazaar
Forking makes a private copy of the Template in your account. The fork is independent — future updates to the original don't flow into your fork.
Use forking when you want to:
- Customize a published Template without publishing your own version.
- Hold a known-good snapshot even if the publisher updates theirs.
- Modify a Template to point at your own [Prop](/concepts/props/) instead of the publisher's.
## Publish to the Bazaar
From a Template you own:
1. Fill in publish-ready metadata — description, category, default values that work without your specific environment.
2. Walk the [publishing checklist](/operate/publishing/).
3. Flip the "publish to Bazaar" toggle on a version.
That version is now public. Future versions you publish appear too. You can unpublish a version at any time; existing forks keep working.
## Quality bar
The Bazaar is a public surface. Before publishing:
- Real test launch from a fresh setup. No leftover state.
- Variables are honest about what launchers must provide. No hidden hardcoded values.
- Screenshots in the launch's mutters so future launchers see success.
- Description explains what the Template does, not just what's in it.
See [Before you publish](/operate/publishing/) for the full checklist.
## Public profile
Templates you publish appear on your public profile alongside any [Genies you've opted into Build in Public](/concepts/agents/#build-in-public). Visitors see what you've made without a Fibe account.
## FAQ
Is the Bazaar moderated?
Yes. Templates flagged for malicious behavior, broken defaults, or misleading descriptions are removed. The publisher is notified.
Can I make my Bazaar Template require payment?
Not today. Bazaar Templates are free to launch. Hosting costs (Marquee usage) still apply to the launcher.
What happens to running Playgrounds if the publisher unpublishes?
Nothing. Running Playgrounds keep running. The Template version is already cloned into your Playspec. You just can't pull future versions from a Template that's no longer public.
## Related
- [Playspecs & Templates](/concepts/props/) — Template authoring lifecycle.
- [Playspecs](/concepts/playspecs/) — what a Bazaar launch produces.
- [Scrolls](/concepts/scrolls/) — Pantry is the private counterpart to the public Bazaar.
- [Before you publish](/operate/publishing/) — publishing checklist.
- **[Fibe Templates](/authoring/overview/)** — full authoring guide.
## Billing
> https://whats.fibe.gg/concepts/billing/
Everything financial — plans, balance, subscriptions, referrals — lives in **Profile → Billing**. Fibe doesn't push monthly subscriptions on you. You hold a balance in two currencies and spend on action.
## Wallet
The Wallet holds your account balance. It's the page you'll see most often under Billing.
### What the Wallet page shows
- **Current balance** in each of the two currencies (see below).
- **History** — every credit and debit, with a description and a link to the resource that triggered it.
- **Pending charges** — anything provisioned but not yet billed (e.g. a Marquee disabled mid-cycle).
- **Promotional credits** tallied separately from paid balance, so you can see what came from grants, [Runes](#runes), or referrals.
### Top up
From the Wallet page:
1. Pick an amount, or pick a top-up pack (bundles at a small discount).
2. Pay via the billing provider shown in the checkout flow.
3. Balance credited — usually immediately, sometimes after the provider clears the transaction.
You can also top up via:
- A **[Rune](#runes)** (an invite or promotional code).
- A **[referral](#referrals)** payout.
- An occasional **grant** the platform issues directly.
### Auto-recharge
Some plan tiers let you set an auto-recharge threshold: when the balance drops below the threshold, the configured top-up pack is billed automatically every N days. The plan card shows the recharge cadence.
Use auto-recharge for production setups where you don't want a Marquee disabled because the balance hit zero.
## Mana
Mana is the **primary** currency. Use it for anything persistent.
### Mana spend categories
- **Marquees** — the base cost per tier. Charged on a recurring cadence as long as the Marquee is active.
- **Top-up packs** — purchased as a bundle.
- **Other persistent infrastructure** the platform exposes — disk, dedicated runner pools, account-level features.
### Plan card
The Wallet shows a plan card per Marquee tier. Each card has:
- **Effective per-Marquee cost** — what you pay per month for one Marquee at this tier.
- **Fuel line** — how many Marquees the balance can fuel for how many days (e.g. "this can fuel 3 Marquees for 14 days").
- **Features** — what the tier includes (CPU/RAM, concurrent Playgrounds, dev-environment capacity).
- **Best-value markers** when one tier is materially cheaper per Marquee than another.
Pick a tier when you add a [Marquee](/concepts/marquees/), or upgrade a tier from the Marquee's own page.
## Sparks
Sparks are the **bursty** currency. Use them for one-off and premium actions.
### Sparks spend categories
- **Premium Genie features** — larger context windows, faster models, higher rate limits inside a session.
- **Trick credits** for paid execution modes (e.g. high-priority queue).
- **One-off purchases** the checkout flow surfaces at the point of use.
### Mana → Sparks conversion
Convert Mana to Sparks at a fixed rate from the Wallet page. One-way: Sparks can't be converted back to Mana.
Convert when you hit a Sparks-only checkout but don't want to top up via the billing provider for a small amount.
## Subscriptions
If you're on a plan that includes recurring entitlements — a managed Marquee, a feature bundle, anything else billed on a cycle — those show up in the **Active Subscriptions** section of the Billing page.
Per-subscription columns:
- **Plan** — what's being subscribed to.
- **Provider** — the billing provider (e.g. card on file, third-party processor).
- **Period** — current billing cycle dates.
- **Status** — active, past due, cancelled, etc.
Manage (upgrade, downgrade, cancel) from the subscription's row.
## Referrals
Share your **referral code** with people who'd benefit from Fibe. When they sign up using your code, both sides get a credit.
The Billing page shows:
- **Your Code** — the referral code unique to your account.
- **Referrals desc** — a short description of the program (terms, payout, current promotion).
- **Referred** — list of accounts that signed up using your code.
Credit posts to your Wallet once the referred account hits the qualifying milestone (typically: first paid top-up or first Marquee).
## Runes
A **Rune** is a single-use code that grants something — a top-up, a credit, a feature unlock, or a tutorial Marquee.
Types of Runes you'll see:
- **Invite Runes** — issued during private beta and special launches.
- **Promotional Runes** — given at events or with marketing campaigns.
- **Compensation Runes** — issued by support when something went wrong.
Redeem from the Billing page. Once redeemed, a Rune is marked **Used** and the granted item posts to your Wallet or activates on your account.
The page shows:
- **Rune** — the code, masked except the prefix.
- **Used** — when (if) it was redeemed.
## What's free
You don't need to spend anything to:
- Sign up.
- Use a tutorial Marquee.
- Author Templates privately.
- Browse the [Bazaar](/concepts/bazaar/).
- Open a standalone Genie chat on a tutorial Marquee.
You start spending when you add your first paid Marquee or buy a premium Sparks-priced action.
## FAQ
Do credits expire?
Paid Mana and Sparks don't expire. Promotional credits (from Runes, referrals, grants) usually do — expiration shown when applied. Check the Wallet history for exact dates.
Refunds?
Unused balance is refundable within a reasonable window after purchase. Specific items follow standard SaaS conventions. The checkout flow shows the policy.
Cheapest way to start?
Tutorial Marquee — free, platform-managed. When ready for real work, top up enough Mana for one Single Marquee for a month.
Sparks needed but I only have Mana?
Convert on the Wallet page at the fixed rate. Immediate. One-way.
What happens if my balance hits zero?
Persistent infrastructure (Marquees) gets disabled — existing Playgrounds keep running until next renewal cycle but new launches are blocked. Top up to re-enable. Auto-recharge prevents this.
Where do I see the invoice for a top-up?
The Wallet history row links to the invoice from the billing provider. Click the row.
## Related
- [Marquees](/concepts/marquees/) — the main Mana spender.
- [Agents](/concepts/agents/) — Sparks consumers (premium model features, Pokes).
- [Advanced → Limits & Quotas](/advanced/limits/) — what your plan-level quotas are.
- [Advanced → Data Backup](/advanced/backup/) — covered by your plan, not a Sparks spend.
## Marquees
> https://whats.fibe.gg/concepts/marquees/
A **Marquee** is a Docker host registered with Fibe. Once connected, it runs as many Playgrounds and Tricks as its capacity allows. Marquees handle TLS, public and internal HTTP routing, registry credentials, and the SSH terminal.
A Marquee must be funded before Fibe can launch, restart, build, stream live logs, SSH, run connection tests, restart routing, schedule work, stop or destroy runtime, or send Genie messages through it. If unpaid, those actions return `MARQUEE_NOT_FUNDED`. Billing and funding screens stay available so you can restore service.
## What a Marquee gives you
| Capability | Detail |
| --- | --- |
| **Docker execution** | Compose files run as containers on the host. Fibe drives Docker; you don't. |
| **Public routing (HTTPS)** | `external` services get a URL under your root domain. TLS terminates at the Marquee. |
| **Internal routing** | `internal` services share the URL shape but sit behind Basic Auth. |
| **DNS at the root domain** | Point a wildcard at the Marquee. Every service gets a subdomain. |
| **Registry credentials** | Add once. Every Playground using those images can pull. |
| **SSH terminal** | Open from the Marquee page. Inspect disk, containers, the Docker daemon. |
| **Capacity** | Many environments side-by-side. Limit is host CPU/memory. |
## Add a Marquee
Connect any Docker-capable host reachable over SSH, or use a managed **tutorial Marquee** to start without infrastructure.
You supply:
- **SSH details** — address, user, key. Fibe installs and operates Docker remotely.
- **Root domain** — every service becomes `.`. Requires a wildcard DNS record.
- **TLS** — automatic (Let's Encrypt) or your own certificate.
- **Optional registry credentials** — Docker Hub, GHCR, ECR, etc.
:::tip Tutorial Marquees
The first Marquee in a new account is typically a tutorial Marquee — pre-provisioned, platform-owned. Use it to learn. Add your own for real work.
:::
## Marquee types
Marquees come in tiers, purchased from your [Wallet](/concepts/billing/). Differences are capacity (CPU, RAM, concurrent Playgrounds), not feature gates.
## Routing & URLs
Two URL kinds per service:
- **Public (`external:PORT`)** — `https://.`. Anyone with the URL reaches it.
- **Internal (`internal:PORT`)** — same shape, Basic Auth in front.
Choice is per-service, via the `fibe.gg/expose` label. Container ports are not published manually. Fibe handles binding, certificates, the proxy.
```yaml
services:
web:
image: nginx:alpine
labels:
fibe.gg/expose: external:80 # → https://web.
admin:
image: my-org/admin:1.0
labels:
fibe.gg/expose: internal:8080 # → https://admin. (Basic Auth)
```
Subdomain defaults to the service name. Override with `fibe.gg/subdomain`. See [Service labels → Routing & exposure](/authoring/service-labels/).
## Health & capacity
The Marquee page shows:
- **Live status** — reachability, Docker daemon, service health.
- **Capacity** — running Playgrounds, CPU/memory usage.
- **Schedule** — Playgrounds and Tricks running here.
- **SSH terminal**.
- **Connection test** — re-runs the check. Surfaces firewall, key, disk problems.
Connection tests and live diagnostics also require a funded Marquee because they contact the remote host.
**Disable a Marquee** for maintenance. No new launches scheduled. Existing Playgrounds keep running.
## Removing a Marquee
Stop or move attached Playgrounds and Tricks first. The product lists what's attached.
Decommission flow:
1. Disable the Marquee.
2. Stop or destroy obsolete Playgrounds.
3. Move long-running Playgrounds to another Marquee.
4. Delete the Marquee. The host machine is untouched.
## Example: connect a DigitalOcean droplet
1. Ubuntu droplet, 2 vCPU / 4 GB RAM minimum.
2. Wildcard DNS `*.dev.example.com → `.
3. **Add Marquee** in Fibe with:
- Host: `dev.example.com`
- SSH user, SSH key.
- Root domain: `dev.example.com`.
- TLS: automatic.
4. **Test connection.** Fibe verifies SSH, installs Docker, opens ports.
5. Marquee ready. Launch a Playground.
## FAQ
How many Marquees can I have?
Plan-dependent. Tutorial Marquees count separately. Top up the [Wallet](/concepts/billing/) for more.
Move a running Playground to a different Marquee?
Yes. "Move to another Marquee" on the Playground page. Fibe stops, copies relevant volume contents, relaunches. Not instantaneous — schedule downtime.
Can two people share a Marquee?
No. Marquees are owned by one Player.
Host goes down?
Connection check flips the Marquee to error. Playgrounds become unreachable until the host returns. No automatic failover.
Does the Marquee see my source?
Source-mounted dev templates: yes — the Marquee clones the repo via Prop credentials. Built images: only the image, not raw source. Either way, source lives on the Marquee for the Playground's lifetime.
## Related
- [Wallet, Mana & Sparks](/concepts/billing/) — how you fund a Marquee.
- [Props](/concepts/props/) — repos Fibe pulls from.
- [Playgrounds](/concepts/playgrounds/) — what runs on Marquees.
- Reference: [`fibe-product-map`](/reference/fibe-product-map/), [`fibe-feature-surface`](/reference/fibe-feature-surface/), [`reference-fibe-labels`](/reference/reference-fibe-labels/).
## Playgrounds
> https://whats.fibe.gg/concepts/playgrounds/
A **Playground** is the running environment launched from a [Playspec](/concepts/playspecs/). Services, URLs, logs, terminal access, an optional Genie panel.
Starting, changing, stopping, or destroying a Playground runtime requires its Marquee to be funded. If billing has expired, these actions return `MARQUEE_NOT_FUNDED`.
## Lifecycle states
| State | Meaning |
| --- | --- |
| **pending** | Queued. Marquee hasn't started provisioning. |
| **in progress** | Images pulling, containers starting, healthchecks settling. |
| **running** | Up. URLs work. |
| **has changes** | Playspec edited. Running Playground hasn't picked them up. Rollout or Hard restart applies. |
| **completed** | Tricks only. Watched service finished. |
| **error** | Launch failed. Logs say why. |
| **destroying** | Tearing down. |
## Actions
- **Rollout** — apply edits with minimum disruption. Unchanged services keep running.
- **Hard restart** — stop everything, start fresh. Use when state has drifted or after structural changes.
- **Stop** — turn off services, keep the Playground record. Restart later.
- **Retry** — re-run a failed launch.
- **Extend** — push expiration out.
- **Destroy** — remove the Playground.
- **Maintenance mode** — route traffic to a maintenance page. Containers stay up. Toggle, not a state.
`force` can bypass some state protections when the server permits it.
:::tip Expiration with uncommitted changes
If a Playground has uncommitted changes when expiration falls due, Fibe holds off and surfaces a warning. Commit, extend, or destroy.
:::
## What the page gives you
- **Service URLs** — public and internal, HTTPS.
- **Live logs** per service.
- **In-browser terminal** per service.
- **Environment overrides** — change values without rebuilding the image.
- **Service discovery** — containers reach each other by service name inside the Compose network.
- **Status timeline** — build → ready.
- **Genie side panel** — chat in context with any configured [Genie](/concepts/agents/).
## Plain Docker Compose
A Playground's body is a Docker Compose file plus a few Fibe additions (labels, optional settings block). Run the same file locally with `docker compose up` — no Fibe service required for local dev.
On a Marquee the Fibe additions activate (routing, source mounting, variables, healthchecks). Locally they're ignored by Compose.
What this means:
- No Fibe install needed for local development.
- Debug a launch by running the Compose against local Docker.
- The recipe stays portable. Fibe is one place to run it, not the only one.
## Data durability
| Where data lives | Survives restart | Use for |
| --- | --- | --- |
| **Named volume** | Yes | Databases, uploads, anything you'd be sad to lose. |
| **External service** (S3, managed DB) | Yes | Production-shaped data. |
| **Container filesystem** | No | Disposable. Gone on rollout. |
A hard restart can pull a fresh image. Pin tags (`postgres:17`, not `postgres:latest`). Renaming services or volume keys can detach existing data — the product warns first.
## FAQ
How long does a Playground stay alive?
Until you stop it, destroy it, or its expiration passes. Expiration is surfaced prominently.
Can I have many Playgrounds at once?
Yes. Limited by Marquee capacity. A small Marquee runs a handful; a bigger one runs many.
Rollout vs Hard restart?
**Rollout** keeps unchanged services running; only changed ones restart. Use for everyday edits.
**Hard restart** stops and starts every service. Use after structural changes (renamed service, volume layout change) or when state has drifted.
502 from outside but works in the container terminal?
Almost always: the service binds to `localhost` instead of `0.0.0.0`. Fix the bind address. See [Common problems](/operate/common-problems/) for per-framework commands.
What does Maintenance mode do?
The Marquee proxy serves a maintenance page for the Playground's URLs. Containers stay up — SSH in, read logs, edit. Toggle it off and routing returns. The Playground's state (running, has_changes, error) is unaffected.
## Related
- [Playspecs](/concepts/playspecs/) — the blueprint that produces a Playground.
- [Marquees](/concepts/marquees/) — where Playgrounds run.
- [Templates](/concepts/playspecs/#templates) — what Playspecs are launched from.
- [Tricks](/concepts/tricks/) — the one-shot variant.
- [Genies inside a Playground](/concepts/agents/) — chat with AI in context.
- Reference: [`fibe-resource-lifecycles`](/reference/fibe-resource-lifecycles/), [`reference-runtime-implied-semantics`](/reference/reference-runtime-implied-semantics/).
## Playspecs
> https://whats.fibe.gg/concepts/playspecs/
A **Playspec** is a launch blueprint — one configured launch, ready to fire. A **Template** is a reusable recipe a Playspec is launched from. Many Playspecs can share one Template. Both manage what a launch becomes, from different angles: the Playspec carries the launch-time values; the Template carries the body.
This page covers both, in that order.
## Playspecs
A Playspec points at a Template version, holds variable values, names a target Marquee, and lists mounted files. Launching it produces a [Playground](/concepts/playgrounds/).
### What a Playspec stores
| Field | What it holds |
| --- | --- |
| **Template Version** | The frozen Template snapshot to launch from. May be omitted if launched from raw YAML. |
| **Variable values** | Inputs the Template asks for. |
| **Marquee** | The host the Playground runs on. |
| **Mounted files** | Optional uploads attached at launch (e.g. seed data, credentials). |
| **Schedule / triggers** | Optional automation (cron, VCS push). Applies to Tricks. |
Re-launch the same Playspec months later and you get an equivalent environment. The Template Version is frozen; the variables are stored; the Marquee is named.
### Launching
Pick a Template (or paste raw YAML), pick a Marquee, fill variables, click Launch. The resulting Playspec is saved. Launching is repeatable from the Playspec itself.
### Editing a Playspec
Edits to a Playspec don't touch the running Playground. The Playground keeps running until you apply the change:
- **Rollout** — apply with minimum disruption.
- **Hard restart** — full stop and start.
- **Deploy** — explicit re-launch.
- **Reconciliation** — Fibe's periodic match-up.
Prep changes in advance, apply on your schedule.
### Switch to a newer Template version
When a new Template version exists, every Playspec bound to that Template can be switched:
1. Open the Playspec.
2. Fibe surfaces the new version with a diff preview.
3. Apply. The Playspec is now bound to the new version. The running Playground picks it up on the next rollout.
You can also **bulk-upgrade** every Playspec bound to one Template at once.
This control only applies to Playspecs with a source Template Version. A Playspec launched from raw YAML has nothing to switch to.
### Launch without a Template
Paste a Compose file into a new Playspec, set variables, go. Use a Template only when you want reuse, sharing, auto-publish from a [Prop](/concepts/props/) file, or [Bazaar](/concepts/bazaar/) publication.
For a one-off run, skip the Template entirely.
## Templates
A Template is a reusable environment recipe — a Docker Compose file plus a few Fibe additions (labels, optional settings block). Author privately, iterate, keep for yourself or publish to the [Bazaar](/concepts/bazaar/).
Templates keep launches reproducible. Two people launching the same Template version get the same starting environment.
For authoring a Template from scratch — Compose-to-Fibe conversion, label and settings-block reference, recipes, playbooks — see the top-level **[Fibe Templates](/authoring/overview/)** section.
### Lifecycle
1. **Create.** Paste a Compose file, import from a connected [Prop](/concepts/props/), fork an existing Template, or take one from the [Bazaar](/concepts/bazaar/).
2. **Publish a Version.** A frozen snapshot of body + variables + metadata + automation settings. New launches default to the latest suitable version.
3. **Existing Playspecs** keep the version they launched from. Switching is explicit — see [Switch to a newer Template version](#switch-to-a-newer-template-version) above.
4. **Source-linked Templates** auto-publish a new version when the linked file changes.
5. **Fork any time.** Forks are independent. They don't track the source.
### Versions are frozen
Published versions can't change. Edits become a new Version with a new ID. This is what makes a Playspec reproducible — it always knows the exact Version it came from.
### Source-linked Templates — the strongest pattern
A Template can point at a file in a [Prop](/concepts/props/). The environment recipe lives in source control alongside your code.
When creating a Template, point it at:
- A specific **Prop**.
- A specific **file path** (usually `docker-compose.yml` at the repo root).
- Optionally a specific **branch** (defaults to the repo default).
Then:
- New commits touching that file **auto-publish a new Template version**. The body is the file at that commit.
- Flip the **CI** toggle and Fibe creates a dedicated Trick that runs against the latest version on push or pull request.
This keeps Templates editable in normal source control — PRs, code review, branch protection — and Fibe stays in sync without pasting.
### `source_defaults`
Inside a Template's metadata, set `source_defaults: true`. Launching the Template from a Prop then auto-fills the repo URL and branch into dynamic services and any `trigger_config`.
File-linking and `source_defaults` are distinct:
- **File-linking** — *where the Template body lives* (in a Prop's file).
- **`source_defaults`** — *how launch-time values are populated* (from the importing Prop).
Use either, both, or neither. See [Fibe Templates → Settings block](/authoring/settings-block/).
### Test before sharing
Before publishing to the [Bazaar](/concepts/bazaar/):
- Real test launch from a fresh setup. No leftover state.
- Confirm variables are honest. No hidden hardcoded values.
- Capture screenshots in the launch's mutters.
- Walk the [publishing checklist](/operate/publishing/).
## FAQ
Template vs Playspec?
A **Template** is a reusable recipe. A **Playspec** is one configured launch — picks a Template Version, fills variables, picks a Marquee, mounts files. Many Playspecs can derive from the same Template.
Can two Playspecs share the same launch values?
Yes. Both can point at the same Template Version with the same variables. They produce equivalent Playgrounds. Each Playground runs independently.
How do I delete a Playspec?
Destroy the Playground first, then delete the Playspec.
How do I delete a Template?
Delete a Template only when no Playspecs are bound to its versions. Delete or unlink the Playspecs first.
Template vs Compose file in the repo?
If the Template is source-linked, the repo file *is* the body. Edits become new Template versions automatically. If not source-linked, the body lives in Fibe. Edit it in the UI.
## Related
- [Props](/concepts/props/) — where source-linked Template bodies live.
- [Playgrounds](/concepts/playgrounds/) — what a Playspec produces when launched.
- [Marquees](/concepts/marquees/) — where Playgrounds run.
- [Bazaar](/concepts/bazaar/) — public Template marketplace.
- [Tricks](/concepts/tricks/) — Playspecs run as one-shot jobs.
- **[Fibe Templates](/authoring/overview/)** — full authoring guide: Compose-to-Fibe, labels, settings block, recipes, playbooks.
- Reference: [`fibe-resource-lifecycles`](/reference/fibe-resource-lifecycles/), [`convert-compose-to-fibe`](/reference/convert-compose-to-fibe/), [`recipe-add-metadata`](/reference/recipe-add-metadata/).
## Props
> https://whats.fibe.gg/concepts/props/
A **Prop** is a connected Git repository. Fibe clones it for builds, mounts it for dev, triggers Tricks on push.
Without a Prop, a Template can still be a static `image:` recipe. With one, the Template gets dynamic — branches, commits, hot-reloaded source, push-triggered jobs.
## What a Prop gives you
| Capability | Detail |
| --- | --- |
| **Branch discovery** | Every branch listed for launchers to pick. |
| **Source for builds** | Templates that say `fibe.gg/repo_url: ` clone at launch. |
| **Live source mount** | Dev services mount the working tree. Edits show up live. |
| **Compose detection** | On connect, Fibe notices `docker-compose*.yml` and `.env.example`. Candidates for new Templates. |
| **Push notifications** | Refresh branch list, fire source-linked Templates that track changed files, trigger listening Tricks. |
| **In-browser git** | Stage, commit, push from the Playground terminal. |
| **Per-commit history** | What landed and when. |
## Supported providers
Two providers, each with its own auth path:
| Provider | URL shape | Auth |
| --- | --- | --- |
| **Built-in Gitea** | URL inside Fibe's Gitea host | Auto-provisioned per Player. No setup. |
| **GitHub** | `https://github.com/owner/repo` or `ssh://…` | GitHub App installation, or per-Prop Personal Access Token. |
Other providers (GitLab, Bitbucket, self-hosted) aren't supported today. The repo-URL validator only accepts the two above. To use code from another host, push a mirror into the built-in Gitea.
## Connect a GitHub repository
Three ways, in order of preference:
### 1. Install the Fibe GitHub App (recommended for orgs)
Best when you have multiple private repos in the same org and want webhooks + CI to work without per-Prop token management.
1. Go to **Profile → Advanced Settings → GitHub Apps**, click **Install on GitHub**.
2. GitHub asks which org/account and which repos to grant.
3. Pick repos. Return to Fibe; the installation is registered.
4. Create a Prop from any granted repo — no token needed. Fibe mints short-lived installation tokens at clone time.
Multiple installations per account are supported (one per org or repo set). Detail: [Advanced → GitHub Apps](/advanced/github-apps/).
### 2. Sign in with GitHub
Best when you mostly use your own (personal) repos and want a single OAuth click.
1. Click **Connect GitHub** from the profile. GitHub OAuth flow runs.
2. Your GitHub user is linked. Public repos clone without further setup; private ones still need an App installation or a PAT.
### 3. Paste a Personal Access Token (per-Prop)
Best for one-off integrations or when you can't install the App (no admin rights on the org).
When creating a Prop from the **GitHub Repository** tab:
- **Repo URL** — `https://github.com/owner/repo` or `owner/repo`.
- **Default branch** — defaults to the repo default.
- **Credentials (Personal Access Token)** — paste a PAT.
- **Classic PAT** with the `repo` scope, **or** a **fine-grained PAT** scoped to the specific repo with the relevant permissions (contents read/write, metadata read).
- Format `ghp_…` (classic) or `github_pat_…` (fine-grained).
- Stored encrypted on the Prop. Used at clone time and for the API calls the Prop needs (read/write contents, metadata).
PATs are per-Prop and don't share across Props. Rotate from the Prop's settings page when the token expires.
When **both** an App installation and a PAT are available, the PAT wins. The Prop uses what's set on its `credentials` field; if blank, it falls back to the App installation token.
## Connect a built-in Gitea repository
Fibe runs an internal Gitea instance and provisions an account for every Player automatically.
### Auto-provisioning
The moment your Player is created, a background job runs:
1. Creates a Gitea user matching your Fibe username.
2. Generates a random password and stores it in your `gitea` provider connection metadata.
3. Mints a Gitea access token with `read/write:repository` and `read/write:user` scopes.
You don't take any action. When the job finishes you'll see a toast: *"Your Gitea account has been provisioned. Check your profile for credentials."*
### Where the Gitea credentials live
Profile page shows a **Gitea account** card:
- **Username** — same as your Fibe username.
- **Password** — the random one generated at provisioning. Copy it from the card. Hidden by default; click to reveal.
- **Profile** — link to your Gitea profile page.
- **Sign in** — link to the Gitea sign-in page. Gitea uses a separate session from Fibe.
Use these to log into Gitea directly (push from the command line, browse the web UI, manage SSH keys, etc.).
### Reset credentials
If you lose the password or want to rotate:
1. Profile → **Reset credentials** on the Gitea card.
2. 2FA confirmation required.
3. Fibe generates a new password and a new access token. The Prop's stored credential is updated; the password is shown again.
### Creating a Gitea repo
From **New Prop → New Repository** tab:
- **Repo name** — kebab-case, lowercase, unique under your Gitea account.
- **Private** — toggle.
- Click **Create Repository**. Fibe creates the repo in Gitea and saves the Prop.
The repo is owned by your Gitea user. You can push to it via HTTPS (Gitea credentials or PAT) or SSH (add a key to your Gitea profile).
### Provisioning failures
Provisioning retries automatically (polynomial backoff, up to 10 attempts for connection issues). If it ultimately fails, Profile shows a **Retry Gitea provisioning** button.
## What's auto-set up on connect
Whatever the provider and auth path, on creation Fibe:
1. Resolves the repo URL. Validates the format.
2. Discovers branches.
3. Notes useful files (`docker-compose*.yml`, `.env.example`) as candidates for new Templates.
4. Installs the webhook on the upstream (GitHub App or Gitea).
## Pushes
On commit, Fibe:
1. Refreshes the branch list.
2. Auto-publishes new Template versions for any source-linked Template tracking a changed file.
3. Fires Tricks configured for that push or PR.
The webhook is managed automatically.
## Editing source from a Playground
Playground terminal has full git tooling. The UI exposes a diff view, commit message field, push button.
Commits go back to the Prop. The webhook fires. Source-linked Templates and Tricks react.
## Props are personal
Each Player owns their Props. Two people can connect the same upstream repository independently, with separate credentials, branches, and Trick configurations.
If you fork a Template that points at someone else's Prop, your fork doesn't get access to their repository. Connect your own Prop (or a fork of theirs).
## Source-linked Templates
A Template can point at a file in a Prop — typically the Compose file at the repo root. Two effects:
1. **Auto-publish on file change.** New commits touching the file → new Template version. Body is the file at that commit.
2. **CI Trick.** Enable CI on the Template and Fibe creates a Trick that runs against the latest version on push or PR.
See [Playspecs → Source-linked Templates](/concepts/playspecs/#source-linked-templates--the-strongest-pattern) for full detail.
## FAQ
Which is better — GitHub App or PAT?
App, when you can install it: installation tokens are short-lived, scoped per-installation, and webhook delivery is managed centrally. PATs are convenient when you can't install the App (no org admin rights) or for one-off Props.
Can I use a PAT for an arbitrary git host (GitLab, Bitbucket, self-hosted)?
No. The `credentials` field works only for GitHub URLs. The URL validator rejects other hosts. To bring code in from elsewhere, push a mirror to the built-in Gitea.
Fine-grained vs classic PAT?
Both work. Fine-grained is preferred — you can scope it to one repo and one set of permissions. Classic PATs grant `repo` scope, which covers all of your repos at once.
What happens when a PAT expires?
Clones and webhook handlers fail. The Prop surfaces an authentication error on the next sync. Paste a fresh PAT into the Prop's Credentials field to fix.
Rotate credentials on a Prop?
Yes. Re-authenticate (or paste a new PAT) from the Prop settings. Old credentials revoked.
Where's my Gitea password if I never saved it?
Profile → Gitea account → reveal Password. If the password isn't visible ("Password is not saved for this account"), click **Reset credentials** to mint a new one.
Can I SSH into Gitea?
Yes. Add your SSH public key to your Gitea profile (Sign in to Gitea → Settings → SSH/GPG Keys). Then `git clone git@:/.git` works.
Deleted Prop?
Existing Playgrounds keep running (source is already on the Marquee). New launches that reference the missing Prop fail with a clear error.
## Related
- [Advanced → GitHub Apps](/advanced/github-apps/) — install and manage GitHub App installations.
- [Marquees](/concepts/marquees/) — where source runs.
- [Playspecs](/concepts/playspecs/) — Templates and the launches they produce.
- [Tricks](/concepts/tricks/) — what fires on pushes.
- Reference: [`recipe-build-to-repo-url`](/reference/recipe-build-to-repo-url/), [`recipe-source-mount`](/reference/recipe-source-mount/), [`mode-trigger-vcs`](/reference/mode-trigger-vcs/).
## Scrolls
> https://whats.fibe.gg/concepts/scrolls/
A **Scroll** is a searchable workspace that collects everything produced around a piece of work — artefacts, mutters, messages, raw provider traces, memories, and Pantry templates — in one place.
Open a Scroll for notes and files, inspect raw provider logs when an AI session needs debugging, or pull a reusable template out of the Pantry.
## What lives in a Scroll
| Item | What it is |
| --- | --- |
| **Artefacts** | Files and notes produced during work — reports, screenshots, mockups, CSVs, snippets, interactive previews. |
| **Mutters** | Short progress notes — what was tried, where things broke, what's left. |
| **Messages** | Conversation messages worth surfacing outside the chat. |
| **Raw provider traces** | The exact request/response logs from a Genie's provider. For debugging an AI session. |
| **Memories** | Persistent notes the Genie or the user marked as worth remembering. |
| **Pantry templates** | Reusable Template snippets stored alongside the work that needed them. |
Everything in a Scroll is indexed and searchable. Filter by type, date, source resource.
## Authoring artefacts
Create an artefact directly inside a Scroll:
- **Title** — your reference.
- **Description** — short summary (optional).
- **Body** — Markdown content. The editor surfaces a body placeholder until you start writing.
Artefacts can also be produced automatically by a Genie's work. Either way they land in the same Scroll and are indistinguishable on read.
## Raw provider traces
When a Genie talks to its underlying provider (Claude, Gemini, OpenAI, etc.), the request and response payloads are captured. Open the trace to see exactly what the provider was sent and what it returned. Useful for debugging a wrong answer, a refused tool call, or a context-window failure.
Traces are sensitive — they contain prompts and replies verbatim. Treat the Scroll containing them like the data it contains.
## Pantry
A Pantry is a collection of reusable Templates stored at the Scroll level. Use it when a Template is too specialized for the public [Bazaar](/concepts/bazaar/) but still worth keeping handy across multiple launches.
Pantry templates can be:
- Edited in place.
- Versioned (new version on each save).
- Imported into a Playspec at launch time.
## Memories
A memory is a tagged note the Genie can refer back to in future conversations. Useful for facts about the project, user preferences, or decisions that should persist across sessions.
Memories are written explicitly — either by the user adding a note, or by the Genie storing a fact when instructed.
## Search
A single search box across the whole Scroll. Returns artefacts, mutters, messages, traces, memories, and Pantry templates that match. Filter by type to narrow.
## FAQ
Scrolls vs Conversations?
A Conversation is a chat thread with a Genie. A Scroll is a workspace that may contain messages from one or more Conversations alongside other items. Conversations focus on dialogue; Scrolls focus on the collected outputs of work.
Can two Scrolls share an artefact?
Each artefact lives in one Scroll. Cross-Scroll views are read-only filters; the underlying artefact has one home.
Are Pantry templates public?
No. Pantry templates are private to the Scroll. To make a Template public, publish to the [Bazaar](/concepts/bazaar/).
## Related
- [Agents](/concepts/agents/) — where most Scroll content originates.
- [Bazaar](/concepts/bazaar/) — public counterpart to private Pantry.
- [Playspecs & Templates](/concepts/props/) — Templates can be authored into a Pantry first, then promoted to Bazaar.
## Tricks
> https://whats.fibe.gg/concepts/tricks/
A **Trick** runs a task and finishes. Same shape as a Playground (Compose, services, logs, terminal) but Fibe treats it as one-shot: one replica per service, no restart on exit, exit code of the watched service decides success.
Use Tricks for **work that should finish**: tests, migrations, backups, scheduled jobs, CI.
## Good for
- Test suites, lint checks.
- Migrations, schema setup.
- Backups, exports.
- Data syncs, cleanups, doc builds.
- Cron jobs.
- CI on push or PR.
## Not for
- Web apps that stay reachable.
- Background workers that loop.
- Hot-reload dev servers.
- Anything that watches and never exits.
If the task should finish → Trick. If it should stay up → [Playground](/concepts/playgrounds/).
## How a Trick decides success
Mark one service as the **watched service** with `fibe.gg/job_watch: "true"`. Its exit defines the result. Zero = success. Non-zero = failure.
Supporting services (databases, caches, queues) start alongside. When the watched service exits, Fibe stops them.
```yaml
services:
db:
image: postgres:17
environment:
POSTGRES_PASSWORD: $$var__DB_PASSWORD
test:
image: node:20
working_dir: /app
labels:
fibe.gg/repo_url: https://github.com/owner/repo
fibe.gg/source_mount: /app
fibe.gg/start_command: npm test
fibe.gg/job_watch: "true" # ← Trick succeeds/fails based on this
depends_on:
db:
condition: service_healthy
x-fibe.gg:
metadata:
job_mode: true # ← marks the template as a Trick
description: "Run the test suite against Postgres"
category: "CI"
```
## What Fibe Applies
Fibe applies one-shot rules. You don't write these:
- Every service set to **one replica**.
- Every service set to `restart: no`.
- Fibe labels stripped from the final compose at compile time.
- `fibe.gg/expose` is forbidden — a Trick isn't there to serve traffic.
What you write:
- A service that **actually exits** when done. No idle loops, no `sleep infinity`.
- The watched-service marker on the one whose exit decides the outcome.
## Plain Compose locally
A Trick is a Docker Compose file with Fibe additions on top. `docker compose up` runs the same file locally for debugging. The only Fibe-specific piece is the watched-service marker.
## Schedules & triggers
| Mode | Settings | Use for |
| --- | --- | --- |
| **Manual** | (none) | Click to run. |
| **Scheduled** | `schedule_config` — cron expression + target Marquee. | Daily backups, hourly syncs, weekly reports. |
| **Triggered** | `trigger_config` — event (`push` or `pull_request`), repo, branch, Prop. | CI on every push, PR test runs. |
Combine both. A Trick can be scheduled and triggered — each event fires its own run.
See [Authoring → Execution modes](/authoring/execution-modes/), [`mode-schedule-cron`](/reference/mode-schedule-cron/), [`mode-trigger-vcs`](/reference/mode-trigger-vcs/).
## Job ENV — credentials for Trick runs
Tricks often need credentials the launcher shouldn't supply each time — deploy tokens, API keys, backup passwords.
Store them as **Job ENV entries**. Two scopes:
- **Global Job ENV** — available to every Trick.
- **Prop-scoped Job ENV** — applies only when the Trick is tied to that repository.
Prefer Job ENV over template variables when a credential is reused across many runs. See [Security → Secret Vault & Job ENV](/advanced/secrets/).
## Example: nightly backup
Scheduled Trick that dumps a database at 03:00 UTC:
```yaml
services:
backup:
image: my-org/backup-tool:1.4
environment:
DB_URL: $$var__DB_URL
S3_BUCKET: backups.example.com
AWS_ACCESS_KEY_ID: $$var__AWS_KEY # Job ENV
AWS_SECRET_ACCESS_KEY: $$var__AWS_SECRET # Job ENV
command: ["./backup-now.sh"]
labels:
fibe.gg/job_watch: "true"
x-fibe.gg:
variables:
DB_URL: {name: "Database URL", required: true}
AWS_KEY: {name: "AWS key", required: true, secret: true}
AWS_SECRET: {name: "AWS secret", required: true, secret: true}
metadata:
job_mode: true
description: "Nightly database backup to S3"
category: "Operations"
schedule_config:
enabled: true
cron: "0 3 * * *"
marquee_id: 1
```
`AWS_KEY` and `AWS_SECRET` come from Job ENV at launch time.
## FAQ
Trick takes longer than its schedule period?
Overlapping runs result. Make the period longer than the max job time, or guard with a lock inside the job, or use a single-replica external queue. Fibe doesn't deduplicate scheduled runs.
Multiple branches on one Trick?
No wildcards. One Trick per branch. Explicit configuration is clearer than wildcard surprises.
`push` vs `pull_request`?
- **push** — fires when the named branch receives a commit.
- **pull_request** — fires when a PR is opened or updated targeting the branch. Code under test is the PR head.
Genie to debug a Trick?
Yes. Open a Genie chat against the Trick's logs. The Trick itself runs on its own — a Genie isn't part of the watched service.
## Related
- [Playgrounds](/concepts/playgrounds/) — long-running counterpart.
- [Authoring → Execution modes](/authoring/execution-modes/).
- [Security → Secret Vault & Job ENV](/advanced/secrets/).
- Reference: [`mode-job-trick`](/reference/mode-job-trick/), [`mode-schedule-cron`](/reference/mode-schedule-cron/), [`mode-trigger-vcs`](/reference/mode-trigger-vcs/), [`playbook-test-runner`](/reference/playbook-test-runner/), [`playbook-cron-scheduled`](/reference/playbook-cron-scheduled/).
---
# Authoring templates
## App playbooks
> https://whats.fibe.gg/authoring/playbooks/
Examples by app shape. Each one shows the before/after diff for the input and explains every line.
For each playbook, the [Reference](/reference/intro/) section has a full skill file with the actual YAML. This page is an index — pick the playbook that matches your app and follow the link.
## By app type
| Shape | Playbook |
| --- | --- |
| **nginx serving static HTML or an SPA** | [`playbook-nginx-static`](/reference/playbook-nginx-static/) |
| **Node.js with hot reload (Vite, Next.js, nodemon)** | [`playbook-nodejs-dev`](/reference/playbook-nodejs-dev/) |
| **Generic web app + Postgres** | [`playbook-postgres-app`](/reference/playbook-postgres-app/) |
| **Python web (FastAPI / Django / Flask)** | [`playbook-python-app`](/reference/playbook-python-app/) |
| **Rails (web + db + redis + jobs + websocket)** | [`playbook-rails-app`](/reference/playbook-rails-app/) |
| **Wiki.js** | [`playbook-wikijs`](/reference/playbook-wikijs/) |
| **WordPress + MariaDB / MySQL** | [`playbook-wordpress`](/reference/playbook-wordpress/) |
| **Multi-service with shared config** | [`playbook-multi-service`](/reference/playbook-multi-service/) |
| **Scheduled cron job** | [`playbook-cron-scheduled`](/reference/playbook-cron-scheduled/) |
| **Test runner on every push or PR** | [`playbook-test-runner`](/reference/playbook-test-runner/) |
## How to use a playbook
1. **Find the closest match** to what you're trying to launch.
2. **Read the before/after diff** to see what changed.
3. **Adapt the variable names** to your app — most playbooks ask for a subdomain, image tag, DB password, etc.
4. **Cross-check the relevant recipes** at the end of each playbook for any extra patterns you need.
5. **Run a preview launch** before publishing.
If none of the playbooks match exactly, find the closest one and combine it with [recipes](/authoring/recipes/) for the differences. Most real-world templates are 80% one playbook + 20% recipes.
## Related
- [Recipes](/authoring/recipes/) — smaller patterns to combine.
- [Compose → Fibe](/authoring/compose-to-fibe/) — the master conversion flow.
- [Before you publish](/operate/publishing/) — the polish checklist.
## Authoring a template
> https://whats.fibe.gg/authoring/overview/
A Fibe template is a Docker Compose file with a few Fibe-specific additions — labels on the services that need them, and an optional settings block at the top for launch variables and template metadata.
If you can write Compose, you can author a Fibe template. The additions are small and additive; a Compose file without them is still a valid Fibe template (just with no Fibe-specific behavior).
## The two ingredients
- **Service labels** under `labels:` on each service. These tell Fibe how to route, build, expose, and watch the service.
- **A settings block** at the root under `x-fibe.gg:`. Holds launch-time variables and template metadata. Optional, but you'll want it once you have anything customizable.
Everything else is plain Docker Compose. A template that doesn't need Fibe features is just a Compose file.
## The smallest template
```yaml
services:
web:
image: nginx:alpine
labels:
fibe.gg/expose: external:80
```
One service, one label. You get a public HTTPS URL with no other setup.
## Where it ships
While you're iterating, the template lives in your private **Templates** collection. When it's polished — clear description, sensible defaults, no hardcoded private values — you can publish it to the **Bazaar** for anyone to launch.
See [Before you publish](/operate/publishing/) for the polish checklist.
## What to read next
- **Coming from an existing `docker-compose.yml`?** Walk [Compose → Fibe](/authoring/compose-to-fibe/) — a nine-step conversion.
- **Authoring from scratch?** Skim [Service labels](/authoring/service-labels/) for the label reference, then [Settings block](/authoring/settings-block/) for variables/metadata.
- **Choosing between modes?** Read [Execution modes](/authoring/execution-modes/) — long-running, Trick, scheduled, triggered.
- **Stuck on a decision?** [Decision guides](/authoring/decisions/) has short opinionated answers.
- **Looking for a pattern?** [Recipes](/authoring/recipes/) — small, copy-pasteable changes.
- **A worked end-to-end example?** [Playbooks](/authoring/playbooks/) — Rails, Node dev mode, WordPress, Wiki.js, and more.
## Compose → Fibe
> https://whats.fibe.gg/authoring/compose-to-fibe/
A nine-step path for taking an existing `docker-compose.yml` and turning it into a Fibe template. The result is **still valid Docker Compose** — you can `docker compose up` it locally — plus the Fibe additions that make it launchable on a Marquee.
## Pick the shape first
```text
your docker-compose.yml + intent
├─ HTTP service(s) someone visits → a long-running web template
├─ background workers + DB only → a long-running app template
├─ one-shot task that exits → a Trick
├─ recurring on a cron schedule → a Trick + schedule
└─ runs on git push / pull request → a Trick + trigger
```
The shape decides what you do with steps 5–7.
## The nine steps
### 1 — Classify each service
For each service, decide whether it uses a prebuilt image (**static**) or comes from a Git repository (**dynamic**). Dynamic services point at a repo URL; static ones just reference an image.
Static services: `postgres:17`, `redis:8`, `nginx:alpine`. Use the image directly.
Dynamic services: your own application code. Either built from a Dockerfile or live-mounted from source for dev mode.
See [`decide-static-vs-dynamic`](/reference/decide-static-vs-dynamic/) for the full rules.
### 2 — Resolve `build:`
A Compose `build:` block becomes a dynamic service. Replace it with the `fibe.gg/repo_url` label and any related build settings (Dockerfile path, branch, target stage, build args).
```yaml
# Before (plain Compose)
services:
web:
build:
context: .
dockerfile: Dockerfile.web
target: production
args:
NODE_ENV: production
# After (Fibe)
services:
web:
image: ghcr.io/owner/repo:latest # placeholder image tag — Fibe builds the real one
labels:
fibe.gg/repo_url: https://github.com/owner/repo
fibe.gg/dockerfile: Dockerfile.web
fibe.gg/build_target: production
fibe.gg/build_args: NODE_ENV=production
```
See [`recipe-build-to-repo-url`](/reference/recipe-build-to-repo-url/).
### 3 — Replace `ports:`
For services that need a URL, use `fibe.gg/expose` instead of Compose port publishing. Fibe handles HTTPS routing and gives you a clean URL.
```yaml
# Before
ports: ["3000:3000"]
# After
labels:
fibe.gg/expose: external:3000 # → https://.
```
Use `internal:` instead of `external:` for services that should be Basic-Auth-gated (admin consoles, dashboards).
See [`recipe-ports-to-expose`](/reference/recipe-ports-to-expose/).
### 4 — Drop incompatible keys
Remove `ports:`, `container_name`, and `hostname:` — Fibe takes care of those. Keep `depends_on`, `volumes`, `environment`, `healthcheck`, `networks`, and `restart` as-is.
See [`recipe-strip-incompatible-keys`](/reference/recipe-strip-incompatible-keys/).
### 5 — Decide rolling updates
For exposed HTTP services that can run multiple replicas concurrently, enable zero-downtime rollouts and add the healthcheck labels Fibe needs to know when a new replica is ready.
```yaml
labels:
fibe.gg/zerodowntime: "true"
fibe.gg/healthcheck_path: /health
fibe.gg/healthcheck_interval: 10s
fibe.gg/healthcheck_timeout: 3s
fibe.gg/healthcheck_retries: 3
fibe.gg/healthcheck_start_period: 60s
```
**Do not** enable on stateful singletons (Postgres, Redis, single-instance Kafka). See [`decide-zero-downtime`](/reference/decide-zero-downtime/).
### 6 — Extract launch variables
Anything the launcher should choose (subdomain, image tag, replica count, credentials) becomes a variable. Generated secrets can be set to `random: true` so the launcher doesn't have to invent one.
```yaml
x-fibe.gg:
variables:
SUBDOMAIN:
name: "Subdomain"
default: "demo"
validation: "/^[a-z][a-z0-9-]*$/"
path: services.web.labels.fibe.gg/subdomain
DB_PASSWORD:
name: "Database password"
required: true
random: true
secret: true
path: services.db.environment.POSTGRES_PASSWORD
```
See [Launch variables](/authoring/variables/).
### 7 — Pick the execution mode
| Shape | Add this |
| --- | --- |
| Long-running HTTP | nothing — the default. |
| Trick | `fibe.gg/job_watch: "true"` on the watched service + `x-fibe.gg.metadata.job_mode: true`. |
| Scheduled | (Trick settings) + `metadata.schedule_config`. |
| Triggered | (Trick settings) + `metadata.trigger_config`. |
See [Execution modes](/authoring/execution-modes/).
### 8 — Add metadata
Fill in the template's description and category. If the template will be launched from a Prop, opt in to source defaults so the repository and branch can be filled in automatically.
```yaml
x-fibe.gg:
metadata:
description: "Wiki.js + Postgres, ready to launch"
category: "Knowledge"
source_defaults: true
```
### 9 — Validate & preview
Run a preview launch before publishing. Many issues only surface at compile or runtime — schema validity isn't the same as a successful launch.
A typical iteration loop:
1. Edit the Template.
2. Click Preview launch on a test Marquee.
3. Watch the build logs.
4. Open the service URL.
5. Repeat until it's clean.
## Quick cheatsheet
| Intent | Add these labels / settings |
| --- | --- |
| Public HTTP from a prebuilt image | `fibe.gg/expose: external:PORT` |
| Internal-only HTTP behind Basic Auth | `fibe.gg/expose: internal:PORT` |
| Build from your repository | `fibe.gg/repo_url` (optionally `fibe.gg/dockerfile`, `fibe.gg/branch`) |
| Live-edit dev mode | `fibe.gg/repo_url` + `fibe.gg/source_mount: /app` + `fibe.gg/start_command` + `fibe.gg/production: "false"` |
| Zero-downtime rollouts | `fibe.gg/zerodowntime: "true"` + the `fibe.gg/healthcheck_*` labels |
| One-shot Trick | `fibe.gg/job_watch: "true"` on the watched service + `x-fibe.gg.metadata.job_mode: true` |
| Subdomain chosen at launch | `fibe.gg/subdomain: $$var__SUBDOMAIN` + matching variable |
| Image tag chosen at launch | `image: ghcr.io/owner/repo:$$var__TAG` + matching variable |
## Example: minimal nginx → Fibe
```yaml
# docker-compose.yml (before)
services:
web:
image: nginx:alpine
ports: ["80:80"]
volumes:
- ./html:/usr/share/nginx/html:ro
# Fibe template (after)
services:
web:
image: nginx:alpine
labels:
fibe.gg/expose: external:80
volumes:
- ./html:/usr/share/nginx/html:ro # mount stays; Marquee preserves it
```
For more, see the [Playbooks](/authoring/playbooks/).
## Related
- [Service labels](/authoring/service-labels/) — the full reference for every `fibe.gg/*` label.
- [Settings block](/authoring/settings-block/) — the full reference for `x-fibe.gg`.
- [Common problems](/operate/common-problems/) — what to do when the conversion misbehaves.
- Reference: [`convert-compose-to-fibe`](/reference/convert-compose-to-fibe/) — the skill version of this same flow.
## Decision guides
> https://whats.fibe.gg/authoring/decisions/
Short answers to the questions you'll ask while authoring.
## Static or dynamic?
A service is **dynamic** if it has a repository URL — either through a Compose `build:` block or the `fibe.gg/repo_url` label. Otherwise it's **static**.
**Choose static when:**
- You consume a published image like `postgres:17`, `redis:8`, or `nginx:alpine`.
- The service is configured through environment variables and volumes.
- The image already contains the runtime command.
**Choose dynamic when:**
- Your app's code lives in a repository and Fibe should clone, build, or live-mount it.
- You want hot-reload by mounting the working tree.
- You want the template to work cleanly across branches.
See [`decide-static-vs-dynamic`](/reference/decide-static-vs-dynamic/).
## How should it be reachable?
| Service | Reachable? |
| --- | --- |
| Public web app | `external:PORT` |
| Internal admin / metrics | `internal:PORT` (Basic Auth) |
| Background worker | not exposed |
| Database / cache / queue | not exposed |
| Auxiliary build-time service | not exposed |
| WebSocket service used by a sibling web app | share a subdomain with a `path_rule` |
:::caution Bind correctly inside the container
An HTTP service must listen on `0.0.0.0`, not `localhost` — otherwise it works from inside the container but returns 502 from the outside.
:::
See [`decide-exposure-strategy`](/reference/decide-exposure-strategy/).
## Should I enable rolling updates?
**Yes, when all are true:**
- The service is exposed.
- It speaks HTTP — a path-based healthcheck makes sense.
- It can run with multiple replicas concurrently (stateless or session-shared).
- It doesn't pin `container_name` or publish ports.
**No, for:**
- Stateful singletons (Postgres, MySQL, SQLite).
- Single-instance caches (Redis, memcached).
- Message brokers (Kafka, RabbitMQ).
- Background workers and other non-HTTP services.
See [`decide-zero-downtime`](/reference/decide-zero-downtime/) and [`recipe-zero-downtime-healthcheck`](/reference/recipe-zero-downtime-healthcheck/).
## Where does this credential go?
| Value | Where to keep it |
| --- | --- |
| App-level launch config (app name, environment label) | template variable |
| Generated DB password unique to a launch | variable with `random: true` |
| External API token (Stripe, OpenAI…) | Secret Vault |
| Credential reused across many Tricks | Job ENV entry |
| Sensitive value the launcher should type each time | variable marked `sensitive` |
:::caution Anti-patterns
- Putting a real secret in `default:` — it lives in source.
- Re-randomizing a database password on every launch — existing data becomes unreachable.
- Asking the launcher to type a long-lived API key every time — use the vault.
:::
See [`decide-secrets-and-randoms`](/reference/decide-secrets-and-randoms/) and [Secret Vault & Job ENV](/advanced/secrets/).
## Long-running, Trick, scheduled, or triggered?
| Shape | When |
| --- | --- |
| Long-running HTTP | Service should stay up. Default. |
| Trick (job mode) | Task should finish. Mark a watched service. |
| Scheduled Trick | Trick + cron schedule for recurring runs. |
| Triggered Trick | Trick + push/PR trigger for CI-style jobs. |
See [Execution modes](/authoring/execution-modes/) and [`decide-job-mode`](/reference/decide-job-mode/).
## Related
- Reference: [`decide-static-vs-dynamic`](/reference/decide-static-vs-dynamic/), [`decide-exposure-strategy`](/reference/decide-exposure-strategy/), [`decide-zero-downtime`](/reference/decide-zero-downtime/), [`decide-secrets-and-randoms`](/reference/decide-secrets-and-randoms/), [`decide-job-mode`](/reference/decide-job-mode/).
## Execution modes
> https://whats.fibe.gg/authoring/execution-modes/
A template runs in one of four shapes. Decide first; everything else follows.
## Long-running HTTP
The default. Just expose your services with `fibe.gg/expose` and they stay up until you stop them. No special settings needed.
## Trick (one-shot)
Required pieces:
- `x-fibe.gg.metadata.job_mode: true`
- `fibe.gg/job_watch: "true"` on at least one service.
Trick rules Fibe enforces for you:
- No service may be exposed — a Trick isn't there to serve traffic.
- Every service is forced to one replica and not to restart.
- Watched services must actually exit (don't run a dev server or sleep loop).
- The Trick succeeds when every watched service exits with status zero; non-zero on any watched service fails the run.
```yaml
services:
test:
image: node:22
working_dir: /app
labels:
fibe.gg/repo_url: https://github.com/owner/repo
fibe.gg/source_mount: /app
fibe.gg/start_command: npm test
fibe.gg/job_watch: "true"
fibe.gg/production: "false"
x-fibe.gg:
metadata:
description: "Run npm test against the repo"
category: "CI"
job_mode: true
```
## Scheduled (cron)
A Trick that fires on a recurring schedule. Add `schedule_config` with `enabled`, a 5-field cron expression, and the target Marquee.
| Cron | Meaning |
| --- | --- |
| `0 3 * * *` | daily at 03:00 |
| `*/5 * * * *` | every 5 minutes |
| `0 */2 * * *` | every 2 hours |
| `0 0 1 * *` | first of every month at midnight |
| `30 6 * * 1-5` | 06:30, Mon–Fri |
:::caution Overlap
If your job can run longer than its period, you'll get overlapping runs. Make the period longer than the maximum job time, or guard with a lock inside the job.
:::
```yaml
x-fibe.gg:
metadata:
job_mode: true
description: "Nightly backup to S3"
category: "Operations"
schedule_config:
enabled: true
cron: "0 3 * * *"
marquee_id: 1
```
## Triggered (push or pull request)
A Trick that fires on a Git event. Add `trigger_config` with the event type (`push` or `pull_request`), the repo URL, branch, the Prop the source is connected through, and the target Marquee.
- **push** fires when the named branch itself receives a commit.
- **pull_request** fires when a PR is opened or updated targeting the branch; the code under test is the PR head branch.
- Wildcards across many branches aren't supported — make a template per shape.
```yaml
x-fibe.gg:
metadata:
job_mode: true
description: "Run tests on every push to main"
category: "CI"
trigger_config:
enabled: true
event_type: push
repo_url: https://github.com/owner/repo
branch: main
prop_id: 1
marquee_id: 1
```
## You can combine schedules and triggers
A Trick can be both scheduled (run every night) and triggered (run on every push). Each event fires its own run.
## Related
- [Tricks & automated jobs](/concepts/tricks/) — the concept page.
- Reference: [`mode-job-trick`](/reference/mode-job-trick/), [`mode-schedule-cron`](/reference/mode-schedule-cron/), [`mode-trigger-vcs`](/reference/mode-trigger-vcs/), [`decide-job-mode`](/reference/decide-job-mode/).
## Launch variables
> https://whats.fibe.gg/authoring/variables/
Two ways to weave a variable's value into the template — **inline** inside a string, or as a **whole-node replacement** at a specific location.
## Inline: `$$var__NAME`
Use it anywhere inside a string — an image tag, a URL, an environment value, a label value.
```yaml
services:
web:
image: ghcr.io/acme/app:$$var__TAG
environment:
DATABASE_URL: "postgres://user:$$var__DB_PASSWORD@db:5432/app"
PUBLIC_URL: "https://$$var__SUBDOMAIN.$$root_domain"
labels:
fibe.gg/expose: external:$$var__PORT
fibe.gg/subdomain: $$var__SUBDOMAIN
```
`$$root_domain` is special — Fibe always replaces it with the launching Marquee's root domain. You don't need to declare it.
## Whole-node: `path:` / `paths:`
Bind a variable to a specific location inside the template. The whole value at that location is replaced.
```yaml
x-fibe.gg:
variables:
REPLICAS:
name: "Web replicas"
default: 2
path: services.web.deploy.replicas
DEBUG:
name: "Debug mode"
default: false
paths:
- services.web.environment.DEBUG
- services.worker.environment.DEBUG
```
See [Variable placement](/authoring/variable-placement/) for the path syntax.
## Which form to choose
| Usage | Best form |
| --- | --- |
| Whole scalar value (env entry, replica count, label value) | `path:` / `paths:` |
| Fragment inside a larger string (URL, tag prefix) | `$$var__NAME` |
| Replacing an existing Compose `${VAR}` reference | `$$var__` |
| Same value in many places | `paths:` with an array |
## Defaulting
When the launcher doesn't supply a value, Fibe uses:
1. The variable's `default`, if set.
2. A generated value if `random: true`.
3. Otherwise, an error if the variable is `required: true`.
## Random values
- Set `random: true` and the launcher doesn't have to supply anything.
- The generated value is **persisted with the launch** and reused on subsequent compiles — your database password doesn't reset every time.
- Mark a variable `secret` or `sensitive` to nudge the launcher UI to mask the value.
## Validation patterns
Constrain what a launcher can type. The validation is a regular expression wrapped in slashes:
```yaml
validation: "/^[a-z][a-z0-9-]*$/" # slug-like
validation: "/^[0-9]+$/" # integer-as-string
validation: "/^[A-Za-z0-9_.-]+$/" # image tag
```
Leave it empty or omit it when any value is fine.
## A example
```yaml
x-fibe.gg:
variables:
APP_NAME:
name: "Application name"
required: true
default: "myapp"
validation: "/^[a-z][a-z0-9-]*$/"
paths:
- services.web.environment.APP_NAME
- services.worker.environment.APP_NAME
SUBDOMAIN:
name: "Subdomain"
default: "demo"
validation: "/^[a-z][a-z0-9-]*$/"
path: services.web.labels.fibe.gg/subdomain
DB_PASSWORD:
name: "Database password"
required: true
random: true
secret: true
paths:
- services.web.environment.DB_PASS
- services.db.environment.POSTGRES_PASSWORD
REPLICAS:
name: "Web replicas"
default: 2
path: services.web.deploy.replicas
DEBUG:
name: "Debug mode"
default: false
paths:
- services.web.environment.DEBUG
```
## Related
- [Variable placement](/authoring/variable-placement/) — what goes in `path:` / `paths:`.
- [Settings block](/authoring/settings-block/) — where `variables:` lives.
- Reference: [`reference-template-variables`](/reference/reference-template-variables/), [`recipe-random-and-secrets`](/reference/recipe-random-and-secrets/).
## Recipes
> https://whats.fibe.gg/authoring/recipes/
Pattern-level building blocks. Each one is a small set of changes that solve a single authoring question.
For each recipe, the [Reference](/reference/intro/) section has a full skill file with the exact YAML. This page is an index — pick the recipe that matches your need and follow the link.
## Routing & exposure
- [Replace `ports:` with `expose`](/reference/recipe-ports-to-expose/) — Compose port publishing → Fibe HTTPS routing.
- [Pick a subdomain](/reference/recipe-add-subdomain/) — set the host label; use `@` for the root.
- [Share a subdomain with a path rule](/reference/recipe-add-path-rule/) — several services, one subdomain.
## Source & build
- [Convert `build:` to repo labels](/reference/recipe-build-to-repo-url/) — Compose builds → Fibe dynamic services.
- [Live source mount](/reference/recipe-source-mount/) — dev-mode templates with hot reload.
- [Build args & targets](/reference/recipe-build-args-and-target/) — multi-stage builds, custom build args.
- [Env file pointer](/reference/recipe-env-file/) — tell Fibe which env example file to read.
## Variables & secrets
- [Lift Compose `${VAR}`](/reference/recipe-extract-env-variables/) — turn Compose interpolations into Fibe variables.
- [Inline a variable into a string](/reference/recipe-inline-variables/) — `$$var__NAME` in URLs, tags, partial strings.
- [Bind a whole node with a path](/reference/recipe-whole-node-paths/) — `path:` / `paths:` for whole values.
- [Generate & mark sensitive](/reference/recipe-random-and-secrets/) — `random: true` for launch-unique secrets.
## Compose hygiene
- [Strip incompatible keys](/reference/recipe-strip-incompatible-keys/) — what to remove.
- [Use named volumes](/reference/recipe-named-volumes/) — for persistent data.
- [Order with `depends_on`](/reference/recipe-depends-on/) — start services in the right order.
- [Share with anchors](/reference/recipe-anchors-and-aliases/) — YAML anchors for repeated config.
- [Inline config files](/reference/recipe-configs-block/) — `configs:` for small init scripts.
- [Fill in metadata](/reference/recipe-add-metadata/) — description, category, source_defaults.
## Scaling & healthchecks
- [Healthcheck labels for rolling updates](/reference/recipe-zero-downtime-healthcheck/) — the five healthcheck labels.
## Related
- [App playbooks](/authoring/playbooks/) — end-to-end conversions by app shape.
- [Compose → Fibe](/authoring/compose-to-fibe/) — the conversion flow.
## Service labels
> https://whats.fibe.gg/authoring/service-labels/
The complete set of `fibe.gg/*` labels you can add under `labels:` on a service. Any unrecognized `fibe.gg/*` label is rejected, so typos surface quickly.
## Source & build
| Label | Purpose |
| --- | --- |
| `fibe.gg/repo_url` | HTTPS GitHub or Gitea URL the service comes from. Required for built services and source-mounted services. |
| `fibe.gg/branch` | Pin to a non-default branch. |
| `fibe.gg/dockerfile` | Dockerfile path relative to the repo root (defaults to `Dockerfile`). |
| `fibe.gg/build_target` | Name of the stage when using a multi-stage build. |
| `fibe.gg/build_args` | Comma-separated `KEY=value` pairs for build-time arguments. |
| `fibe.gg/source_mount` | Container path to live-mount the working tree at (defaults to `/app`). |
| `fibe.gg/start_command` | Command to run, overriding the image's default. For dev-mode templates this is where you put your hot-reload command (`bin/rails server -b 0.0.0.0`, `next dev -H 0.0.0.0`, `vite --host 0.0.0.0`, `uvicorn app:main --reload --host 0.0.0.0`). |
| `fibe.gg/env_file` | Path to the env example file in your Prop (defaults to `.env.example`). Fibe reads this file to surface what env values your service expects; your service still loads the file itself at runtime. |
| `fibe.gg/production` | Set to `"true"` for built images, `"false"` for source-mounted dev mode. When it's false, Fibe mounts your repository into the container so edits show up live — pair it with `fibe.gg/start_command` set to your dev/watch command so reloads actually happen. |
## Routing & exposure
| Label | Purpose |
| --- | --- |
| `fibe.gg/expose` | Make the service reachable. Use `external:PORT` for a public HTTPS URL, `internal:PORT` for an internal URL gated by Basic Auth, or just the bare port number. |
| `fibe.gg/subdomain` | Choose the subdomain under the Marquee's root domain. Lowercase letters and digits, optional hyphens. Use `@` to bind at the root domain itself. Defaults to the service name. |
| `fibe.gg/path_rule` | Share a subdomain across multiple services using path matchers like `Path`, `PathPrefix`, and `PathRegexp`. Combine with `&&` or `||`. |
## Rolling updates
Set `fibe.gg/zerodowntime: "true"` when the service is exposed, speaks HTTP, and can run multiple replicas at once. Then add the healthcheck labels so Fibe knows when a new replica is ready:
| Label | Purpose |
| --- | --- |
| `fibe.gg/healthcheck_path` | HTTP path that returns 2xx when the service is ready. |
| `fibe.gg/healthcheck_interval` | How often to poll (`10s`, `500ms`, `1m`). |
| `fibe.gg/healthcheck_timeout` | How long to wait for a response — keep it shorter than the interval. |
| `fibe.gg/healthcheck_retries` | Consecutive failures that mark the replica unhealthy. |
| `fibe.gg/healthcheck_start_period` | Grace window during boot — match real boot time. |
## Automation
| Label | Purpose |
| --- | --- |
| `fibe.gg/job_watch` | Set to `"true"` on the service whose exit defines the result of a Trick. |
## Booleans: quote them
Label values that look boolean (like `"true"` or `"false"`) should be written as **quoted strings**. Avoid `yes`, `no`, `on`, `off`, `1`, or `0` — only the literal strings are recognized.
```yaml
labels:
fibe.gg/production: "true"
fibe.gg/zerodowntime: "false"
```
## Cross-label rules
The runtime enforces these consistency rules:
- A Compose `build:` block **requires** `fibe.gg/repo_url`.
- `fibe.gg/source_mount` **requires** `fibe.gg/repo_url`.
- `fibe.gg/zerodowntime: "true"` **requires** `fibe.gg/expose`, and the service **must not** declare `ports:` or `container_name`.
- Any label value can be a variable reference using `$$var__NAME`.
## A example
A Rails dev-mode service with source mount, dev server command, healthcheck, and rolling updates disabled (since Rails dev mode is single-process):
```yaml
services:
web:
image: ruby:3.3
working_dir: /app
labels:
fibe.gg/repo_url: https://github.com/owner/rails-app
fibe.gg/source_mount: /app
fibe.gg/start_command: bin/rails server -b 0.0.0.0 -p 3000
fibe.gg/expose: external:3000
fibe.gg/subdomain: app
fibe.gg/production: "false"
fibe.gg/env_file: .env.example
depends_on:
- db
```
## Related
- [Compose → Fibe](/authoring/compose-to-fibe/) — the conversion flow.
- [Settings block](/authoring/settings-block/) — `x-fibe.gg` reference.
- [Execution modes](/authoring/execution-modes/) — `job_watch` in context.
- Reference: [`reference-fibe-labels`](/reference/reference-fibe-labels/) — the authoritative skill.
## The x-fibe.gg settings block
> https://whats.fibe.gg/authoring/settings-block/
An optional root key on the template. Plain Docker Compose ignores it (anything beginning with `x-` is a vendor extension), so the file remains a valid Compose document for ordinary `docker compose up` testing.
## Shape
```yaml
x-fibe.gg:
variables: { ... } # launch-time inputs
metadata:
description: "..." # what this template launches
category: "..." # a broad, discoverable category
source_defaults: true|false # auto-fill repo/branch on import
job_mode: true|false # mark this as a Trick template
schedule_config: { ... } # cron-driven launches
trigger_config: { ... } # VCS-triggered launches
```
:::info Where execution settings live
Execution settings (`job_mode`, `schedule_config`, `trigger_config`) live under `metadata`. Anything you put at the root is treated as a compatibility mirror but not authoritative.
:::
## variables
A map of launch-time inputs keyed by variable name. Each entry can carry:
- `name` — the display label shown in the launcher (required).
- `required` — must the launcher supply a value?
- `default` — used when no value is provided.
- `random` — generate a value automatically (good for first-launch passwords).
- `validation` — pattern the value must match (slash-wrapped regex).
- `path` or `paths` — where the value is written into the template body.
- `secret` / `sensitive` — UI hints for the launcher.
See [Launch variables](/authoring/variables/) for the full details.
## metadata
```yaml
metadata:
description: "Production-ready Wiki.js + Postgres"
category: "Productivity"
source_defaults: true
```
`source_defaults: true` tells Fibe: when this template is launched from a connected Prop, fill in repository URLs and branches automatically on dynamic services that don't have them.
## schedule_config
```yaml
schedule_config:
enabled: true
cron: "0 * * * *"
marquee_id: 1
```
Combined with `job_mode: true`. Cron is a standard 5-field expression. Fibe resolves `marquee_id` to a Marquee you own.
## trigger_config
```yaml
trigger_config:
enabled: true
event_type: push # or "pull_request"
repo_url: "https://github.com/owner/repo"
branch: "main"
prop_id: 1
marquee_id: 1
```
Used with `job_mode: true`. With `source_defaults: true` and a source-backed launch, the repo URL and branch can be filled in for you.
## Example: full settings block
A Wiki.js template ready for the Bazaar:
```yaml
x-fibe.gg:
variables:
SUBDOMAIN:
name: "Subdomain"
default: "wiki"
validation: "/^[a-z][a-z0-9-]*$/"
path: services.wiki.labels.fibe.gg/subdomain
DB_PASSWORD:
name: "Database password"
required: true
random: true
secret: true
paths:
- services.wiki.environment.DB_PASS
- services.db.environment.POSTGRES_PASSWORD
metadata:
description: "Wiki.js with Postgres, ready to launch with one click"
category: "Productivity"
source_defaults: false
```
## Related
- [Launch variables](/authoring/variables/) — the inside of `variables:`.
- [Variable placement](/authoring/variable-placement/) — paths under templates.
- [Execution modes](/authoring/execution-modes/) — `job_mode`, schedule, trigger settings.
- Reference: [`reference-x-fibe-gg-namespace`](/reference/reference-x-fibe-gg-namespace/).
## Variable placement (paths)
> https://whats.fibe.gg/authoring/variable-placement/
When you bind a variable to a location with `path:` or `paths:`, the location is a dotted reference into the template body.
## Typical paths
```text
services.web.environment.RAILS_ENV
services.web.deploy.replicas
services.web.labels.fibe.gg/expose
services.web.labels.fibe.gg/subdomain
x-fibe.gg.metadata.description
services.web.environment[0]
services.web.command[2]
```
Square brackets index into arrays. Dotted keys like `fibe.gg/expose` are matched as **single segments** under `labels:` even though they contain dots — Fibe knows the difference.
## Same value, many destinations
```yaml
DB_PASSWORD:
name: "Database password"
required: true
random: true
paths:
- services.postgres.environment.POSTGRES_PASSWORD
- services.pgbouncer.environment.DB_PASSWORD
- services.web.environment.FIBE_DB_PASS
- services.jobs.environment.FIBE_DB_PASS
```
One random value, four destinations. All four read the same value at launch time.
## How writes are typed
Fibe infers the type of the written value:
- All-digit strings → integers.
- The literals `true` and `false` → booleans.
- Anything else → strings.
If you need a literal `"3"`, supply the value with quotes via a different mechanism — a path write of `3` will become an integer.
## Useful behaviors
- **Path writes happen after any inline substitution.** If both target the same value, the path write wins.
- **Missing intermediate maps are created for you** — the path doesn't need to already exist.
- **Indexing into something that isn't an array** is treated as a no-op, not an error — the rest of the template still compiles.
## Related
- [Launch variables](/authoring/variables/) — what's in a variable definition.
- Reference: [`reference-yaml-paths`](/reference/reference-yaml-paths/).
---
# Operate
## Before you publish
> https://whats.fibe.gg/operate/publishing/
A checklist for templates that will appear in the [Bazaar](/concepts/playspecs/#templates). Walk it once before you click publish; re-walk it on major updates.
The goal is to make sure that someone clicking your Template in the Bazaar gets a successful launch — first try — without having to read your mind.
## The basics
- The YAML parses cleanly.
- The root has a `services:` map.
- The template validates and a preview launch succeeds.
- The template would start on a plain Docker host if you supplied equivalent env values yourself.
## Service modeling
- Static services use `image:` only.
- Dynamic services declare `fibe.gg/repo_url`.
- Source-mounted services have a sensible `working_dir` and source mount path.
- Databases, caches, and queues use named volumes — never host paths.
- Use `depends_on` wherever startup order matters, but expect the app to retry transient failures.
- No fixed `container_name`, especially on services with rolling updates.
## Labels & routing
- No unrecognized `fibe.gg/` labels.
- Boolean labels written as quoted `"true"` / `"false"`.
- Exposure values are lowercase; ports in valid range or supplied via a variable.
- Public HTTP goes through `fibe.gg/expose`, never Compose `ports:`.
- Custom subdomain only when the default isn't right.
- Path rules use only path matchers — no host, header, method, query, or client-IP rules.
- Internal-only services use `internal:PORT` for Basic Auth protection.
## Rolling updates (only if eligible)
- The service is exposed.
- It speaks HTTP and supports multi-replica concurrency.
- It has a real HTTP healthcheck endpoint.
- No fixed container name, no Compose `ports:`.
- Realistic healthcheck values — timeout shorter than interval, start period long enough for actual boot.
## Variables
- Every variable has a clear display name.
- Defaults are safe — pasteable into a fresh launch as-is.
- Validation patterns are slash-wrapped.
- Whole-value bindings use `path:` or `paths:`; inline is for fragments only.
- No real secrets in `default:`. Use `random: true` for generated values; use the Secret Vault for external credentials.
- No hardcoded passwords anywhere — in environment values, config files, or scripts.
- Every `$$var__` reference has a declaration; no unused declarations.
## Metadata
- The description explains what the template launches and who it's for.
- The category is broad enough to be discoverable.
- Execution settings (`job_mode`, schedule, trigger) only appear when they apply.
- For triggered templates, the event type is `push` or `pull_request`.
## Runtime quality
- Web services bind to `0.0.0.0` inside the container.
- Dev templates use watch / dev commands — and the description says so.
- Production templates avoid source mounts unless intentional.
- Required env values come from defaults, variables, env files, or platform secrets — never from "the launcher knows".
- Migrations and one-time setup are explicit (a dedicated `setup` service or built into the app's command).
- Healthchecks don't depend on external internet.
- Images pinned to a stable tag — no `:latest` in production templates.
## Security rejects
:::caution Don't publish a template that…
- Publicly exposes admin consoles without authentication. Use `internal:PORT` if there's no built-in auth.
- Hardcodes real secrets, customer-specific paths, or private URLs.
- Mounts the Docker socket.
- Runs privileged containers without a documented reason.
- Requires non-generic host paths like `/data/...` or `/mnt/...`.
- Publishes non-HTTP ports for external access — Fibe routing is HTTPS.
:::
## Frequent slip-ups
- Compose `ports:` left in instead of `fibe.gg/expose`.
- An uppercase `External` in the exposure value.
- `yes` / `on` / `1` used in place of `"true"`.
- A host matcher inside a path rule.
- Variables referenced but never declared, or declared and never used.
- Expecting container env values to drive labels — labels are read before the container starts.
- Rolling updates enabled on a stateful singleton like Postgres.
## After you publish
- Test a launch from a clean account with a fresh Marquee. Required variables should be honest about what the launcher must provide.
- Capture a mutter with screenshots so future launchers see what success looks like.
- Plan a refresh cadence — pinned image tags drift, and a template that worked a year ago may need a touch-up.
## Related
- [Templates](/concepts/playspecs/#templates) — Template versioning + how publishing works.
- [Authoring → Service labels](/authoring/service-labels/) — the rules being checked.
- [Common problems](/operate/common-problems/) — what to do when something fails.
- [`fibe templates` CLI](/sdk/cli-reference/) — managing templates from the command line.
- [`fibe greenfield`](/sdk/workflows/) — one-call automation for full-stack setup.
- Reference: [`templates-publish-checklist`](/reference/templates-publish-checklist/) — the skill version of this checklist.
## Common problems & fixes
> https://whats.fibe.gg/operate/common-problems/
What the message means, and the smallest change that resolves it.
## Template validation
| Message | Fix |
| --- | --- |
| **Unknown `fibe.gg/` label** | You typed something Fibe doesn't recognize. Remove it or fix the spelling. Labels not under the `fibe.gg/` prefix pass through untouched. |
| **Service has a build directive but no `fibe.gg/repo_url`** | Add the repo URL, or remove the Compose `build:` block. |
| **`source_mount` without a `repo_url`** | Same fix; live mount needs to know where the source is. |
| **Zero-downtime services must have an `expose`** | Rolling updates are for routed services; add `fibe.gg/expose`. |
| **Zero-downtime services cannot have `ports:` or `container_name`** | Replicas can't share a pinned name or publish the same host port. Drop them. |
| **Invalid repo URL** | Use an HTTPS GitHub or Gitea URL. SSH URLs aren't accepted. |
| **Invalid exposure visibility** | Only the lowercase strings `internal` and `external` work. `External:3000` (uppercase E) is wrong. |
| **Invalid exposure port** | Must be a real port between 1 and 65535. |
| **Invalid subdomain** | Lowercase letters, digits, hyphens; no leading or trailing hyphen; or the special `@` for the root domain. |
| **Invalid path rule** | Only path matchers (`Path`, `PathPrefix`, `PathRegexp`) are allowed. Host, header, method, query, and client-IP matchers belong to Fibe and aren't authoring-side. |
| **Invalid healthcheck duration** | Use values like `30s`, `500ms`, or `1m`; hours and days aren't accepted. |
| **Boolean label not recognized** | Quote it as `"true"` or `"false"`; don't use `yes`, `no`, `on`, `off`, `1`, or `0`. |
## Variables
| Message | Fix |
| --- | --- |
| **Variable referenced but not declared** | You wrote `$$var__NAME` somewhere but never declared `NAME` under `variables:`. |
| **Variable declared but never used** | Either reference it inline somewhere, give it a `path:`/`paths:` binding, or remove the declaration. |
| **Variable missing a name** | Every variable needs a non-empty `name:` for the launcher UI. |
| **Validation pattern not wrapped in slashes** | Write the regex inside slashes, like `"/^[a-z]+$/"`. |
| **Validation pattern doesn't parse** | Fix the expression itself; the pattern is invalid regex. |
| **Required variable is missing** | Supply a value at launch, add a default, or set `random: true`. |
| **Value fails the validation pattern** | Fix the value, loosen the pattern, or remove it. |
## Triggers & schedules
| Message | Fix |
| --- | --- |
| **Trigger doesn't fire** | Check that the trigger is enabled, the Prop has a working git webhook, and the event type matches what you're actually doing (pull request vs. push to that branch). |
| **Scheduled job doesn't fire** | Check enabled status, verify the cron expression with a quick external tool, and confirm the target Marquee is up. |
| **Resource ID not found** | The Prop or Marquee referenced in trigger/schedule settings doesn't exist or isn't owned by you anymore. Re-pick a current one. |
## Runtime & lifecycle
| Message | Fix |
| --- | --- |
| **Compose `${VAR}` left in the output** | Fibe doesn't fill these from the launcher. Convert to `$$var__VAR` and declare it. |
| **Trick runs forever** | Your watched service started a dev server, an idle loop, or a long-poll. Replace it with a command that exits when the work is done. |
| **Long-running app reset to one replica and never restarting** | You accidentally set `job_mode: true` on something that should stay up. Remove it. |
| **502 from the public URL but works inside the container** | Your app is binding to `localhost`. Switch to `0.0.0.0`. See the table below. |
| **Vite says "Invalid Host header"** | Vite 6+ rejects unknown hosts. Set `server.allowedHosts: true` in your Vite config. |
| **Healthcheck returns 200 too early** | The new replica is being marked ready before the app actually is. Tighten the check so it reflects real readiness. |
| **Image upgrade broke the volume** | A floating tag like `:latest` rolled across a major version and the on-disk format changed. Pin the image to a specific minor version. |
## Subtle behaviors worth knowing
- Mark a template as a Trick and Fibe **forces every service to one replica and no automatic restart** — even ones you didn't list as the watched service.
- **Hostnames in the template are stripped at compile time**; service-to-service traffic uses Compose's built-in DNS by service name.
- When the **same variable is both inlined and path-bound**, the path write is the final word.
- The `fibe.gg/*` **labels are read before the container starts**. You can't configure them with environment values that only exist inside the running container.
## Where the app should bind
| Framework | Correct bind |
| --- | --- |
| Rails | `bin/rails server -b 0.0.0.0` |
| Node / Express | `app.listen(PORT, '0.0.0.0')` |
| Next.js | `next dev -H 0.0.0.0` |
| Vite | `vite --host 0.0.0.0` |
| Django dev server | `python manage.py runserver 0.0.0.0:8000` |
| FastAPI / uvicorn | `uvicorn app:main --host 0.0.0.0` |
| Flask dev | `flask run --host 0.0.0.0` |
## Related
- Reference: [`common-errors-and-fixes`](/reference/common-errors-and-fixes/) — the skill version of this same content.
- [Authoring → Service labels](/authoring/service-labels/) — label rules and conflicts.
---
# SDK, CLI & MCP
## Authentication & profiles
> https://whats.fibe.gg/sdk/authentication/
The CLI, Go library, and MCP server all authenticate the same way: every call carries either an API key (preferred for automation) or a session token obtained through a browser login (preferred for interactive use).
You can keep multiple credentials at once — staging, production, a teammate's view — by giving each one a **profile name**. Switching between them is one command.
## Two ways to log in
### Browser device-code (interactive)
```sh
fibe auth login
```
The CLI prints a short code and opens your browser to a Fibe URL. You confirm the code in the browser, the CLI polls for completion, and your session is stored on disk. No API key to copy around; the flow handles minting a token for you.
This is the right path on a personal machine.
### API key (scripted / CI / agent-accessible)
If you already have an API key (created from your account's [API keys page](/advanced/api-keys/)):
```sh
fibe login --api-key "fibe_live_yourkeyhere"
```
This validates the key against `/api/me`, saves it to your profile, and is ready immediately. Good for CI runners, scripts, and AI agents that need a stable credential.
You can pre-set the key without an interactive prompt by piping it on stdin or via the env var (see "Environment variables" below).
## Profiles
A profile is a named bundle of `{api_key | session_token, domain, default_marquee?}`. Profiles let you switch between accounts and environments without re-logging-in.
```sh
fibe auth list # show profile names
fibe auth use staging # switch the active profile
fibe auth status # who am I, which profile, which domain
fibe --profile staging playgrounds list # one-off override
fibe logout # forget the active profile
```
Common patterns:
- A `default` profile for your main account.
- A `staging` profile pointing at a non-production Fibe deployment.
- A `ci-readonly` profile holding a restricted API key for scripted reads.
## Where credentials live
Credentials are stored under `~/.config/fibe/`:
| File | Contents |
| --- | --- |
| `credentials.json` | API keys and session tokens (the secret stuff) |
| `config.json` | Profile metadata: names, domains, default marquee, last-used timestamp |
Treat `credentials.json` like an SSH private key: never commit it, never paste it into a chat. The Go library and the MCP server read the same files when run on the same machine.
## Environment variables
Useful for CI, Docker containers, and anywhere a profile on disk isn't available:
| Variable | Purpose |
| --- | --- |
| `FIBE_API_KEY` | API key to use. Overrides whatever's in the active profile. |
| `FIBE_DOMAIN` | API domain. Defaults to `https://fibe.gg`. Useful for staging/local. |
| `FIBE_OUTPUT` | Default output format (`table` / `json` / `yaml`). |
| `FIBE_MCP_TOOLS` | Which tool surface to expose (`full`, `core`, or a comma-separated list of tiers). |
| `FIBE_MCP_YOLO` | Set to `1` to skip the confirmation gate on destructive MCP tools. |
| `FIBE_MCP_REQUIRE_AUTH` | Multi-tenant MCP: reject requests without `Authorization: Bearer`. |
When env vars are set, they take precedence over the active profile. When neither is set, the CLI uses the active profile. When neither is set **and** there's no active profile, every call fails with a clear "you need to log in" error.
## CI usage
A typical GitHub Actions step:
```yaml
- name: Trigger nightly trick
env:
FIBE_API_KEY: ${{ secrets.FIBE_API_KEY }}
run: |
fibe tricks trigger --playspec-id 42 --from-file inputs.json
```
Mint a **scoped, expiring** API key for CI (see [Granular resource restriction](/advanced/api-keys/)). Don't reuse your personal-account key for automation; it's a bigger blast radius if it leaks.
## Switching domains
For staging or a self-hosted Fibe:
```sh
fibe auth login --domain https://fibe.staging.example.com
# or, ad-hoc:
FIBE_DOMAIN=https://fibe.staging.example.com fibe playgrounds list
```
`--profile` and `--domain` can be combined in any CLI call without changing the active profile.
## MCP-mode authentication
When you run `fibe mcp serve` for an AI agent, three auth options stack:
1. **Inherits the active profile** by default. The CLI's `default` profile is the MCP server's identity.
2. **Per-request `Authorization: Bearer ` header** when running in HTTP/SSE mode — each requester carries their own credentials.
3. **`fibe_auth_set` tool** — a connected agent can switch credentials at run time without restarting the server.
For a hosted multi-tenant MCP server, set `FIBE_MCP_REQUIRE_AUTH=1` so no one can call tools without supplying a key.
## FAQ
How do I rotate an API key?
Mint a new one in the [API keys page](/advanced/api-keys/), update your profile (`fibe login --api-key NEWKEY`), then revoke the old one. The CLI exits with a clear error if a call uses the revoked key, so you'll know immediately if something is still using it.
Can a Genie use the same credentials as me?
Only if you've explicitly minted an **agent-accessible** API key — that's a key flag set at creation time. By default, your keys are not exposed to Genies. See [API keys → Keys your Genies can use](/advanced/api-keys/).
What if I forget which profile I'm using?
`fibe auth status` shows the active profile and what account it points at. `fibe doctor` runs a deeper check.
How does the CLI tell I'm logged in?
It looks (in order): `FIBE_API_KEY` env var → active profile → fail with "no credentials". Run `fibe doctor` to see exactly what it picked.
## Next step
You're authenticated. Time for the [CLI reference](/sdk/cli-reference/) — every command, grouped by resource.
## CLI reference
> https://whats.fibe.gg/sdk/cli-reference/
`fibe` follows a `fibe [flags]` pattern. This page is the reference for every resource family. For deep workflow stories, see [Common workflows](/sdk/workflows/).
Every command supports `--help` (`fibe playgrounds --help`, `fibe playgrounds create --help`). Use it liberally; this page is the overview.
Commands that launch, restart, inspect, stream from, schedule onto, stop, destroy, clean up, or message a Marquee require that Marquee to be funded. Unpaid Marquees return `MARQUEE_NOT_FUNDED`.
## Global flags
These work on every command:
| Flag | Purpose |
| --- | --- |
| `--api-key ` | Override the API key for this call. |
| `--domain ` | Override the API domain. |
| `--profile ` | Use a specific profile for this call. |
| `--debug` | Verbose logging of HTTP traffic. |
| `-o, --output ` | Output format: `table` (default), `json`, `yaml`. Env: `FIBE_OUTPUT`. |
| `--only ` | Filter fields in JSON/YAML output. |
| `--page `, `--per-page ` | Pagination on list commands. |
| `-f, --from-file ` | Load a JSON or YAML payload from a file or `-` for stdin. |
| `--explain-errors` | Print structured error output (tool family, request ID, hint). |
## Playgrounds
Long-running environments. Full lifecycle from the command line.
```sh
fibe playgrounds list
fibe playgrounds get
fibe playgrounds create --name "demo" --playspec-id 5
fibe playgrounds update -f patch.json
fibe playgrounds delete
fibe playgrounds rollout # apply Playspec edits, minimal disruption
fibe playgrounds hard-restart # stop/start everything
fibe playgrounds stop
fibe playgrounds start
fibe playgrounds maintenance enable
fibe playgrounds maintenance disable
fibe playgrounds extend --by 4h
fibe playgrounds status
fibe playgrounds compose # the compiled compose for the running env
fibe playgrounds logs [--service web] [--since 10m]
fibe playgrounds env # injected env values
fibe playgrounds debug # comprehensive diagnostics
```
The shorthand `pg` works wherever `playgrounds` does: `fibe pg list`.
## Tricks
One-shot jobs.
```sh
fibe tricks list
fibe tricks trigger --playspec-id 12 --from-file inputs.json
fibe tricks get
fibe tricks logs
```
`fibe tricks trigger` accepts the same payload shape the API does. Use `--explain-errors` if a trigger fails — the error typically points at a missing variable or an unreachable repo.
## Agents (Genies)
```sh
fibe agents list
fibe agents get
fibe agents create --name "sys-op" --provider claude-code
fibe agents update -f patch.json
fibe agents delete
fibe agents duplicate # clone with a new name
fibe agents chat --text "Hello" # one-shot message
fibe agents start-chat # establish a long-lived session
fibe agents restart-chat
fibe agents purge-chat
fibe agents runtime-status # reachability, queue depth, last activity
fibe agents watch # follow messages in real time
fibe agents upload-attachment --file context.zip
fibe agents download-attachment --to ./context.zip
fibe agents add-mounted-file --path docs/style.md --content @style.md
fibe agents update-mounted-file -f patch.json
fibe agents remove-mounted-file
fibe agents pokes # list pokes attached
fibe agents messages # message history
fibe agents activity # event timeline
fibe agents defaults # per-Player defaults for new agents
fibe agents gitea-token # mint a short-lived Gitea token
fibe agents authenticate # device-code re-auth for an agent
```
The shorthand `ag` works wherever `agents` does.
## Playspecs, Props, Marquees, Templates
The bookkeeping resources around a launch.
```sh
fibe playspecs list
fibe playspecs get
fibe playspecs create -f playspec.json
fibe props list
fibe props get
fibe props create --repo-url https://github.com/owner/repo
fibe props delete
fibe marquees list
fibe marquees get
fibe marquees update -f patch.json
fibe templates list
fibe templates get
fibe templates search --query "Rails"
fibe templates launch --template-id --marquee-id --vars KEY=VAL
fibe templates change -f patch.json # advanced — patch a template
```
`fibe launch` is a top-level convenience wrapper around `fibe templates launch` — useful in shell scripts because it accepts the same flags more concisely.
Template launch, greenfield, tricks, agent chat, and playground commands all require the selected Marquee to be funded.
## Repos & installations
GitHub and Gitea integration.
```sh
fibe github-repos create --owner my-org --name new-repo
fibe gitea-repos create --owner my-org --name new-repo
fibe github apps connect # print the GitHub App install URL
fibe installations list # GitHub Apps installed on your account
fibe repo-status # is this URL reachable / private / fork
```
## Secrets, Job ENV, API keys, Webhooks
The credentials and event-subscription surfaces.
```sh
fibe secrets list
fibe secrets create --name OPENAI_API_KEY --value "$(pbpaste)"
fibe secrets update -f patch.json
fibe secrets delete
fibe job-env list
fibe job-env create --scope global --name DEPLOY_KEY --value "$(pbpaste)"
fibe api-keys list
fibe api-keys create --name "ci-deploy" --scopes "launch:write" --expires "2026-01-01"
fibe api-keys revoke
fibe webhooks list
fibe webhooks create -f webhook.json
fibe webhooks test
fibe webhooks delete
```
Managing secrets, API keys, and webhooks requires sudo re-auth on the underlying API; the CLI prompts you for your 2FA when needed.
## Artefacts, Mutters, Feedback
The trail your work leaves behind.
```sh
fibe artefacts list
fibe artefacts get --to ./out/ # download an artefact
fibe mutters list
fibe mutter create --playground-id 12 --body "Healthcheck flapping; investigating."
fibe feedbacks list
fibe feedbacks get
```
## Monitoring, audit log, status
```sh
fibe monitor # follow live activity stream
fibe monitor --resource playgrounds # filter by family
fibe audit-logs list
fibe audit-logs get
fibe status # account dashboard
fibe server-info # API version, server-side build info
fibe wait playground 12 --status running --timeout 5m
fibe wait trick 99 --status completed --timeout 1h
```
## Greenfield & launch shortcuts
Launch an existing repo config, or use a repo config as a greenfield snapshot template.
```sh
fibe github apps connect
fibe launch owner/repo --marquee-id 12
fibe launch https://github.com/owner/repo --ref main --file deploy/fibe.yml
fibe greenfield owner/repo --marquee-id 12
fibe greenfield owner/repo@feature/foo --name custom-name
```
Both commands discover config files in this order: `fibe.yml`, `fibe.yaml`, `docker-compose.yml`, `docker-compose.yaml`. `--name` is optional and inferred from the repo basename after slug normalization; pass it explicitly when you want a stable custom name. If your account has multiple GitHub App installations, add `--github-account ` or `--github-installation-id `.
## Local playground inspection
If you're running a Marquee locally and want to peek at `/opt/fibe/playgrounds`:
```sh
fibe local playgrounds info
fibe local playgrounds link # symlink into the current directory
```
## Schema introspection
For agents (or you) who want to know what fields a resource has:
```sh
fibe schema list # all resource families
fibe schema show playground # show JSON schema for one
```
## Config & utility
```sh
fibe config get # dump the current config
fibe config set output yaml # change defaults
fibe doctor # self-check
fibe version
fibe docs # open docs in a browser
```
## Output formats
For scripts, switch to JSON:
```sh
fibe playgrounds list -o json | jq '.[].id'
# Filter to specific fields
fibe playgrounds list -o json --only id,name,status
```
Combine with `jq` for ad-hoc analysis, or pipe `-o yaml` to `yq` for richer queries.
## Reading commands from a file
The `-f` flag accepts a path, `-` for stdin, or `@path` for templated input. Useful when you've assembled a payload from another tool:
```sh
echo '{"name":"demo","playspec_id":5}' | fibe playgrounds create -f -
fibe agents update sys-op -f /tmp/sys-op-patch.yaml
```
## Next step
For programmatic access, the [Go library](/sdk/go-library/) gives you the same surface in Go. For AI agents, the [MCP server](/sdk/mcp-server/) exposes 42 typed tools.
## Common workflows
> https://whats.fibe.gg/sdk/workflows/
End-to-end stories using the [CLI](/sdk/cli-reference/), [Go library](/sdk/go-library/), and [MCP tools](/sdk/tools-catalog/). Pick the workflow that matches what you're doing.
## Launch: existing repo to running Playground
Use this when a GitHub repo already contains a Fibe-compatible `fibe.yml`, `fibe.yaml`, `docker-compose.yml`, or `docker-compose.yaml`.
### Via the CLI
```sh
fibe github apps connect
fibe launch owner/repo --marquee-id 1
fibe launch https://github.com/owner/repo --ref main --file deploy/fibe.yml
```
The GitHub App connection is required even for public repos because Fibe fetches the config server-side. `--ref` selects only the config-file revision; service branch behavior still comes from the template itself. If the repo basename is not the name you want, add `--name`.
### Via MCP
For an AI agent:
```jsonc
// the agent calls:
{
"tool": "fibe_launch_create",
"args": {
"repository_url": "owner/repo",
"github_ref": "main",
"config_path": "deploy/fibe.yml",
"marquee_id_or_name": 1
}
}
```
See [`fibe_launch_create`](/reference/tools/launch-create/) for the full parameter list.
## Greenfield: repo snapshot to app-owned repos
Use this when the repo is a starting template. Fibe reads the selected config once, creates new app-owned destination repo(s), then launches normally.
```sh
fibe greenfield owner/repo --marquee-id 1
fibe greenfield owner/repo@feature/foo --name my-app --github-account me
```
For an AI agent:
```jsonc
{
"tool": "fibe_greenfield_create",
"args": {
"repository_url": "owner/repo@feature/foo",
"name": "my-app",
"marquee_id_or_name": 1,
"git_provider": "github"
}
}
```
`--github-account` and `--github-installation-id` select the GitHub App installation used to read the source config. `git_provider: "github"` controls where the new destination repos are created and uses the player's GitHub OAuth connection.
See [`fibe_greenfield_create`](/reference/tools/greenfield-create/) for the full parameter list.
## Brownfield transform: rewrite an existing Playground
"I want this Playground but with a different template / new repos / changed services — without losing its ID and URL." That's [`fibe_playgrounds_transform`](/reference/tools/playgrounds-transform/).
```jsonc
{
"tool": "fibe_playgrounds_transform",
"args": {
"playground_id": 42,
"template_yaml": "",
"repos": [
{ "repo_url": "https://github.com/me/new-service", "alias": "auth" }
],
"rollout": true,
"wait": true
}
}
```
Behind the scenes: provisions any new repos, authors a new template version, switches the Playspec to it, rolls out, and waits. The Playground's ID stays the same — bookmarks and integrations still work.
## Multi-step pipelines
`fibe_pipeline` runs several MCP tool calls in sequence, threading results between them via JSONPath. Useful when the agent wants one atomic operation instead of round-tripping multiple individual calls.
```jsonc
{
"tool": "fibe_pipeline",
"args": {
"steps": [
{
"id": "make_pg",
"tool": "fibe_launch_create",
"args": {
"repository_url": "me/auth-service",
"github_ref": "main",
"marquee_id_or_name": 1
}
},
{
"id": "wait_ready",
"tool": "fibe_playgrounds_wait",
"args": {
"id": "{{ .make_pg.playground_id }}",
"status": "running",
"timeout": "5m"
}
}
],
"return": "{{ .make_pg }}"
}
}
```
Pipelines support `parallel: [...]` blocks for steps that can run concurrently and `for_each: ...` for repeating a sub-pipeline over an array. Results are cached for 5 minutes — `fibe_pipeline_result` looks up a cached run by ID.
For an LLM agent, this is cheaper than separate tool calls because the launch, wait, and return shape live in one plan.
## Live monitoring & alerting
Tail events as they happen:
```sh
fibe monitor # CLI
```
Or, from an agent:
```jsonc
{ "tool": "fibe_monitor_follow", "args": { "resource": "playgrounds" } }
```
The agent gets progress notifications as new events arrive. Pair it with `fibe_mutter` to post a note when something interesting happens — that's the basis of "babysitting" workflows where an agent watches a Playground and chimes in on noteworthy events.
## CI integration
Mint a scoped API key, set it as a CI secret, run `fibe` from a workflow. Example: a GitHub Actions job that triggers a Trick on push and waits for the result.
```yaml
name: Deploy to staging
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
env:
FIBE_API_KEY: ${{ secrets.FIBE_API_KEY }}
steps:
- name: Install fibe
run: |
curl -L https://github.com/fibegg/sdk/releases/latest/download/fibe_Linux_x86_64.tar.gz | tar xz
sudo mv fibe /usr/local/bin/
- name: Trigger deploy trick
id: deploy
run: |
set -e
OUT=$(fibe tricks trigger --playspec-id 42 \
--vars BRANCH=${{ github.ref_name }} -o json)
ID=$(echo "$OUT" | jq -r '.id')
echo "trick_id=$ID" >> $GITHUB_OUTPUT
- name: Wait for completion
run: fibe wait trick ${{ steps.deploy.outputs.trick_id }} --status completed --timeout 30m
- name: Get logs on failure
if: failure()
run: fibe tricks logs ${{ steps.deploy.outputs.trick_id }}
```
The API key here is narrowly scoped: `launch:write` + `tricks:read` only, with granular restriction to one specific Playspec. If it leaks, the blast radius is one Trick.
## Babysit a long-running Trick
For a CI workflow that waits but also reports progress, use `--follow` semantics:
```sh
fibe tricks trigger --playspec-id 42 -o json | jq -r '.id' | xargs -I {} sh -c '
fibe tricks logs {} --follow &
fibe wait trick {} --status completed --timeout 1h
'
```
Or, from a Go program with the library: spawn the trick, then start `Logs(ctx, id, LogOptions{Follow: true})` in one goroutine and `Wait(ctx, id, "completed", 1*time.Hour)` in another. Cancel one when the other returns.
## Webhook-driven automation
Subscribe a webhook to "Trick completed" events; let the receiver decide what's next (post to Slack, file a ticket, kick off another Trick). The CLI manages webhook subscriptions:
```sh
fibe webhooks create -f - <<'JSON'
{
"url": "https://relay.example.com/fibe-trick-done",
"event_families": ["tricks"],
"event_filters": { "status": "completed" },
"signing_secret": "rotate-this-monthly"
}
JSON
```
See [Webhooks](/advanced/webhooks/) for the full subscription model.
## Switching environments (staging ↔ production)
Maintain two profiles and switch with one command:
```sh
fibe auth login --profile staging --domain https://fibe.staging.example.com
fibe auth login --profile prod --domain https://fibe.gg
fibe auth use staging
fibe playgrounds list # against staging
fibe --profile prod playgrounds list # one-off against prod, doesn't change active
```
Combined with environment-specific API keys, this is the cleanest way to keep both worlds reachable from one machine.
## Build something custom
The Go library is the right answer when you need to build something Fibe doesn't have a CLI command for: a custom dashboard, a Slack bot that surfaces Playground status, a backup tool, a synthetic-traffic generator. See [Go library](/sdk/go-library/).
## Next step
When things go sideways, [Troubleshooting](/sdk/troubleshooting/) covers what to look at.
## Go library
> https://whats.fibe.gg/sdk/go-library/
The Go library at `github.com/fibegg/sdk/fibe` is the same code the CLI uses internally — same retries, same circuit-breaker, same rate-limit handling, same structured errors. Embed it in your own Go programs when you want fine-grained control without shelling out to the CLI.
This page is the orientation. Per-method API docs live at [pkg.go.dev/github.com/fibegg/sdk/fibe](https://pkg.go.dev/github.com/fibegg/sdk/fibe).
## Install the library
```sh
go get github.com/fibegg/sdk/fibe
```
Pin to a tag for reproducible builds. The library follows semver.
## Construct a client
```go
package main
import (
"context"
"log"
"os"
"github.com/fibegg/sdk/fibe"
)
func main() {
client, err := fibe.NewClient(
fibe.WithAPIKey(os.Getenv("FIBE_API_KEY")),
// optional:
fibe.WithDomain("https://fibe.gg"),
fibe.WithTimeout(30*time.Second),
fibe.WithUserAgent("my-app/1.2"),
)
if err != nil { log.Fatal(err) }
ctx := context.Background()
user, err := client.Me(ctx)
if err != nil { log.Fatal(err) }
log.Printf("logged in as %s", user.Email)
}
```
`NewClient` reads from the same `~/.config/fibe/` profile system the CLI uses when no `WithAPIKey` is supplied. So the same machine, with the same profile active, gives you the same identity.
## Resource managers
Each resource family hangs off the client:
| Manager | Operates on |
| --- | --- |
| `client.Playgrounds` | Playgrounds (long-running environments) |
| `client.Tricks` | One-shot job runs |
| `client.Playspecs` | Launch blueprints |
| `client.Agents` | Genies |
| `client.Templates` | Reusable environment recipes |
| `client.Props` | Connected Git repositories |
| `client.Marquees` | Docker hosts |
| `client.Secrets` | Secret Vault entries |
| `client.JobEnv` | Job ENV entries |
| `client.APIKeys` | Your own API keys |
| `client.Webhooks` | Outbound event subscriptions |
| `client.Artefacts` | Generated outputs |
| `client.Mutters` | Progress notes |
| `client.Feedbacks` | Reviews on artefacts |
| `client.AuditLogs` | Read-only history |
| `client.Monitor` | Live event stream |
The common shape of a manager is:
```go
type PlaygroundsManager interface {
List(ctx context.Context, opts ListOptions) ([]Playground, error)
Get(ctx context.Context, idOrName string) (*Playground, error)
Create(ctx context.Context, in *PlaygroundCreate) (*Playground, error)
Update(ctx context.Context, id int64, patch *PlaygroundPatch) (*Playground, error)
Delete(ctx context.Context, id int64) error
Rollout(ctx context.Context, id int64) (*Playground, error)
HardRestart(ctx context.Context, id int64) (*Playground, error)
Stop(ctx context.Context, id int64) (*Playground, error)
Start(ctx context.Context, id int64) (*Playground, error)
Logs(ctx context.Context, id int64, opts LogOptions) (io.ReadCloser, error)
Wait(ctx context.Context, id int64, status string, timeout time.Duration) (*Playground, error)
}
```
(Exact signatures live in godoc. The shape above is the pattern.)
## Built-in robustness
You don't manage retries or backoff yourself. The default configuration includes:
- **Automatic retry** on transient errors (5xx, network resets) with exponential backoff and jitter.
- **Idempotency-key generation** for `Create` and `Update` — safe to call the same method twice in a row.
- **Circuit breaker** that opens when the upstream is sustained-failing, so your program doesn't hammer a sick API.
- **Rate-limit awareness** — when the server returns a rate-limit header, the client sleeps for the indicated retry-after window.
- **Structured errors** — every error implements an interface that lets you ask `IsNotFound(err)`, `IsRateLimited(err)`, `RequestID(err)`, etc., instead of string-matching.
You can swap any of these by passing options to `NewClient`. Example: a tighter retry policy for a CI runner that should fail fast:
```go
client, _ := fibe.NewClient(
fibe.WithAPIKey(key),
fibe.WithRetryPolicy(fibe.RetryPolicy{Max: 1, BaseDelay: 200 * time.Millisecond}),
)
```
## Example — launch a Playground and stream logs
```go
ctx := context.Background()
pg, err := client.Playgrounds.Create(ctx, &fibe.PlaygroundCreate{
Name: "demo",
PlayspecID: 42,
MarqueeID: 1,
})
if err != nil { log.Fatal(err) }
// Wait until it's reachable.
if _, err := client.Playgrounds.Wait(ctx, pg.ID, "running", 5*time.Minute); err != nil {
log.Fatal(err)
}
// Stream logs.
stream, err := client.Playgrounds.Logs(ctx, pg.ID, fibe.LogOptions{Follow: true, Service: "web"})
if err != nil { log.Fatal(err) }
defer stream.Close()
io.Copy(os.Stdout, stream)
```
## Example — trigger a Trick and report results
```go
trick, err := client.Tricks.Trigger(ctx, &fibe.TrickTrigger{
PlayspecID: 99,
Vars: map[string]string{"BRANCH": "main"},
})
if err != nil { log.Fatal(err) }
final, err := client.Tricks.Wait(ctx, trick.ID, "completed", 30*time.Minute)
if err != nil { log.Fatal(err) }
if final.Status != "completed" || final.ExitCode != 0 {
log.Fatalf("trick failed: exit %d", final.ExitCode)
}
```
## Testing with `fibetest`
The `github.com/fibegg/sdk/fibetest` package ships a mock client and a transport that records HTTP traffic for table-driven tests. Use it to test code that calls the SDK without touching a real Fibe.
```go
import "github.com/fibegg/sdk/fibetest"
func TestMyFlow(t *testing.T) {
rec := fibetest.New(t)
rec.OnGet("/api/playgrounds/12").Respond(fibetest.JSON(`{"id":12,"name":"demo","status":"running"}`))
client := rec.Client()
pg, _ := client.Playgrounds.Get(context.Background(), "12")
if pg.Status != "running" { t.Fatal("expected running") }
}
```
Tests run offline; no network, no Fibe account needed.
## Concurrency
The client is safe to share across goroutines. Each call is independent; the rate limiter and circuit breaker coordinate across goroutines so you don't have to.
For workloads doing many parallel calls, increase `WithMaxConcurrency` or wrap calls in `errgroup.Group` — the client respects context cancellation.
## When the CLI is enough
You don't have to embed the library for every job. If you're doing one-off automation, the CLI with `-o json` and a bit of `jq` is often simpler:
```sh
fibe playgrounds list -o json --only id,status | jq '.[] | select(.status=="error")'
```
Reach for the Go library when you need typed responses, structured error handling, or you're embedding Fibe deep in another product.
## Next step
For AI agents that should drive Fibe themselves, [run the MCP server](/sdk/mcp-server/) and let them call typed tools.
## Install the fibe CLI
> https://whats.fibe.gg/sdk/install/
The `fibe` binary is the same single executable whether you use it as a CLI, a Go library dependency, or an MCP server. Pick whichever install method fits your environment.
## Homebrew (macOS, Linux)
The easiest path for individual machines:
```sh
brew install fibegg/sdk/fibe
# or, two-step if you prefer:
brew tap fibegg/sdk
brew install fibe
```
This installs the latest release and keeps it up to date with `brew upgrade`.
## `go install`
If you have a Go toolchain handy:
```sh
go install github.com/fibegg/sdk/cmd/fibe@latest
```
This installs into `$(go env GOBIN)` (typically `~/go/bin`). Make sure that's on your `PATH`.
Use `@latest`, a tag (`@v1.2.3`), or a commit SHA. For reproducible CI, pin to a tag.
## Prebuilt release binaries
For systems without Homebrew or Go, download a binary from the project's [releases page](https://github.com/fibegg/sdk/releases). Builds are published for:
- macOS — amd64, arm64
- Linux — amd64, arm64
- Windows — amd64
```sh
# Linux example
curl -L https://github.com/fibegg/sdk/releases/latest/download/fibe_Linux_x86_64.tar.gz | tar xz
sudo mv fibe /usr/local/bin/
fibe version
```
## Docker
Pull the `e2e` image when you want a runnable `fibe` without installing anything on the host. Useful for CI containers and short-lived shells:
```sh
docker run --rm -it ghcr.io/fibegg/sdk:latest fibe version
```
For day-to-day use, native install is faster — Docker adds a per-invocation startup cost.
## Verify the install
```sh
fibe version
fibe doctor
```
`fibe doctor` runs a self-check: connectivity to the Fibe API, validity of any cached credentials, basic environment sanity. If it's green you're ready to go. If it's red, the output points at the problem (no API key, wrong domain, network, expired token).
## Shell completion
Tab-completion makes a noticeable difference for a tree this wide. Generate completion scripts:
```sh
# Bash (current shell)
source <(fibe completion bash)
# Bash (persistent — Linux)
fibe completion bash | sudo tee /etc/bash_completion.d/fibe
# Zsh (with Oh-My-Zsh / Prezto)
fibe completion zsh > "${fpath[1]}/_fibe"
# Fish
fibe completion fish | source
# PowerShell
fibe completion powershell | Out-String | Invoke-Expression
```
After this, `fibe pl` completes to `fibe playgrounds`, and so on through every subcommand and flag.
## Upgrading
- **Homebrew**: `brew upgrade fibe`
- **Go install**: `go install github.com/fibegg/sdk/cmd/fibe@latest`
- **Release binary**: download the new tarball and overwrite the existing binary.
The CLI is backward-compatible across patch versions; minor versions occasionally add new commands and tools. Major versions only happen with notable API changes — they're called out in the changelog.
## Next step
You have a binary. Now [authenticate](/sdk/authentication/) it so it can talk to Fibe.
## MCP server
> https://whats.fibe.gg/sdk/mcp-server/
The `fibe` binary doubles as an **MCP server** — exposing the same resource surface as the CLI and library, but as **typed tools** an AI agent can call directly. No subprocess overhead, no shell parsing, no string-matching the help text. Your agent gets a real tool API.
42 tools across 9 families. Each one is documented individually under [Reference → Tools](/reference/tools/playgrounds-transform/). The [Tools catalog](/sdk/tools-catalog/) page is the table-of-contents.
## Run the server
```sh
# Stdio (default) — for local single-tenant agents like Claude Code.
fibe mcp serve
# SSE — Server-Sent Events over HTTP. Multi-tenant capable.
fibe mcp serve --transport sse --port 4400
# HTTP — Plain HTTP, JSON-RPC. Multi-tenant capable.
fibe mcp serve --transport http --port 4400
```
The server inherits your active CLI profile by default. For multi-tenant deployments, see "Multi-tenant auth" below.
Tools preserve funding failures as structured MCP errors. When a target Marquee is unpaid, tools return `MARQUEE_NOT_FUNDED` with status `402`.
## Install into a client
The `fibe mcp install` helper writes the right config snippet for popular clients:
```sh
fibe mcp install --client claude-code
fibe mcp install --client claude-desktop
fibe mcp install --client cursor
fibe mcp install --client vscode
fibe mcp install --client antigravity
fibe mcp install --client codex
```
If your client isn't on that list, the snippet is small enough to write by hand. The `mcp install` command also prints what it added — copy-paste it into a config file the client picks up.
### Manual configuration (Claude Code example)
```jsonc
{
"mcpServers": {
"fibe": {
"command": "fibe",
"args": ["mcp", "serve"],
"env": {
"FIBE_API_KEY": "fibe_live_yourkeyhere" // or omit and use the default profile
}
}
}
}
```
Restart the client; it picks up the server on start. Tools appear as `fibe_*` in the agent's tool list.
## Tool surface filters
42 tools is a lot for an agent to keep in working memory. You can narrow the surface:
```sh
FIBE_MCP_TOOLS=core fibe mcp serve # essential tools only (~12 tools)
FIBE_MCP_TOOLS=full fibe mcp serve # everything (default)
FIBE_MCP_TOOLS=meta,resource fibe mcp serve # specific tiers
```
The categories are listed on the [Tools catalog](/sdk/tools-catalog/) page. Most agents do fine with `core` for everyday work and `full` only when they need the deeper escape hatches.
## What the tools cover
Each family has its own group on the [Tools catalog](/sdk/tools-catalog/) page, but the headline:
| Family | Examples |
| --- | --- |
| **Auth & Meta** | `fibe_auth_set`, `fibe_doctor`, `fibe_status`, `fibe_schema`, `fibe_help`, `fibe_tools_catalog`, `fibe_call`, `fibe_run` |
| **Resource CRUD** | `fibe_resource_list`, `_get`, `_mutate`, `_delete`, `_watch` |
| **Playgrounds** | `fibe_playgrounds_wait`, `_logs`, `_logs_follow`, `_action`, `_debug`, `_transform` |
| **Agents** | `fibe_agents_duplicate`, `_runtime_status`, `_send_message`, `_start_chat` |
| **Greenfield** | `fibe_launch_create`, `fibe_greenfield_create`, `fibe_templates_launch`, `_search`, `_change`, `fibe_github_repos_create`, `fibe_gitea_repos_create` |
| **Monitoring** | `fibe_monitor_list`, `_follow`, `fibe_mutter`, `_mutters_get`, `_feedbacks_*` |
| **Local dev** | `fibe_local_playgrounds_info`, `_link` |
| **Pipelines** | `fibe_pipeline`, `_pipeline_result` (multi-step composition) |
| **Repos** | `fibe_find_github_repos`, `_get_github_token`, `_repo_status_check` |
The full annotated list is on [Tools catalog](/sdk/tools-catalog/).
Any tool that launches, rebuilds, streams, inspects, schedules, stops, deletes, cleans up, or talks to a Marquee requires that Marquee to be funded.
## Multi-tenant auth
When you host the MCP server somewhere shared (a SaaS, a team relay, a Cloudflare Worker), every connecting agent needs its own credentials.
Three options stack:
1. **`Authorization: Bearer ` HTTP header** — the agent sends its key with each request. Use this when the agent's API key is known up front.
2. **`fibe_auth_set` tool** — the agent calls this tool first to set its credentials for the rest of the session. Useful when the credential is discovered at run time (e.g. fetched from a vault).
3. **Default profile fallback** — single-tenant; not for shared deployments.
Force the multi-tenant model with:
```sh
FIBE_MCP_REQUIRE_AUTH=1 fibe mcp serve --transport http --port 4400
```
With that env var set, the server refuses any request that doesn't carry credentials. No accidental privilege escalation from the host's default profile.
## The "yolo" flag
By default, destructive tools (delete, rollout, transform) prompt the agent for confirmation through a protocol-level confirm step. For known-good automation, you can skip it:
```sh
FIBE_MCP_YOLO=1 fibe mcp serve
```
Use this sparingly. The confirm step exists because LLM agents occasionally request more than they should — losing it on a multi-tenant server means a confused agent can delete things.
## Progress notifications
Long-running tools (`fibe_playgrounds_logs_follow`, `fibe_monitor_follow`, `fibe_pipeline`) emit MCP-protocol progress events while they work. Compatible clients (Claude Code, Cursor) render these as a streaming status indicator in the chat.
The agent gets a "still going, here's what I've seen so far" experience instead of a long silent wait.
## Pipelines
`fibe_pipeline` is the most powerful tool in the catalog. It runs multiple Fibe calls in sequence, threads results between them via JSONPath bindings, can run blocks in parallel, supports `for_each` loops, and caches results for 5 minutes.
A typical use: "create a new Prop, then create a Playspec from a template against it, then launch a Playground from the Playspec, then wait for it to be ready, then return the URL". One tool call, one result, with full backoff and error handling under the hood.
See [Common workflows → Pipelines](/sdk/workflows/) for an example.
## Inspect what's available
The agent itself can introspect:
```
fibe_tools_catalog # list every tool with descriptions
fibe_schema # ask for the JSON schema of a tool's args
fibe_help # CLI help for an equivalent command
```
These are first-class tools so an agent that doesn't know what you're asking can figure it out.
## Troubleshooting
| Symptom | Fix |
| --- | --- |
| The client can't find `fibe` | Make sure it's on `PATH` for the user the client runs as. `command: "/usr/local/bin/fibe"` in the config is sometimes needed. |
| Calls fail with "no credentials" | Either the profile isn't set, or `FIBE_API_KEY` is missing for the env. Run `fibe doctor` outside the client to confirm. |
| Tools list is empty | Check `FIBE_MCP_TOOLS` — it might be set to a tier with no matching tools. |
| Agent gets stuck waiting | The agent probably has the destructive-confirm gate enabled. Either confirm in the client UI or set `FIBE_MCP_YOLO=1` if you trust the workflow. |
| `Authorization` header isn't being honored | Make sure `FIBE_MCP_REQUIRE_AUTH=1` and the transport is `http`/`sse` (stdio is single-tenant). |
## Next step
The [Tools catalog](/sdk/tools-catalog/) is the table of contents for every tool. From there, each tool's detail page is one click away.
## The Fibe SDK
> https://whats.fibe.gg/sdk/intro/
The Fibe SDK is the **interface to Fibe** — a single Go binary that runs in three modes depending on who's calling:
- **CLI**: you, at a terminal. `fibe playgrounds list`, `fibe agents chat`, `fibe trick run`.
- **Go library**: programs you write in Go that embed Fibe directly. Automate deployments, build a custom dashboard, write a control plane.
- **MCP server**: AI agents (Claude Code, Cursor, Antigravity, anything that speaks the Model Context Protocol). Agents call `fibe_*` tools and Fibe responds.
All three share the same authentication, the same resource model, the same retry / circuit-breaker / rate-limit logic. Pick your interface; the platform is the same.
## When to use which mode
| You are… | Use… |
| --- | --- |
| At a terminal | The **CLI**: `fibe ...` |
| A Go program that needs to drive Fibe | The **Go library** at `github.com/fibegg/sdk/fibe` |
| An LLM agent (Claude Code, Cursor, Codex, etc.) | The **MCP server**: `fibe mcp serve` and 42 tools |
| Writing a one-off shell script | The **CLI** with `-o json` for parseable output |
| Building CI/CD automation | The **CLI** from a workflow, with an API key |
| Embedding Fibe in your own SaaS | The **Go library** |
## The 60-second tour
```sh
# Install (macOS / Linux)
brew install fibegg/sdk/fibe
# First-time setup
fibe login # browser device-code flow
fibe doctor # check connectivity + auth
# Daily use
fibe playgrounds list
fibe agents chat my-genie --text "Hello"
fibe tricks trigger --playspec-id 42 --from-file inputs.json
# Run an MCP server for your AI agent
fibe mcp serve
fibe mcp install --client claude-code
```
That's the whole product surface, in a paragraph.
## What it talks to
The SDK is a **client** to the Fibe API. It doesn't run anything itself; it tells the platform what to do. On the other end is Fibe, which orchestrates [Marquees](/concepts/marquees/) (your Docker hosts), [Props](/concepts/props/) (your Git repos), [Templates](/concepts/playspecs/#templates), [Playgrounds](/concepts/playgrounds/), [Tricks](/concepts/tricks/), and [Genies](/concepts/agents/).
So the SDK is what gets you those resources from a script, an agent, or a CI job — same as the web UI gets them from a browser.
## The three modes in detail
### CLI
```sh
fibe [flags]
```
A standard command tree. Every top-level resource family has the usual list / get / create / update / delete plus action-specific subcommands. Output defaults to a readable table; `-o json` and `-o yaml` produce parseable output for scripts. Many commands accept `-f file.json` to load a payload from a file or `-` for stdin.
Read on: [Install the CLI](/sdk/install/), [Authentication](/sdk/authentication/), [CLI reference](/sdk/cli-reference/).
### Go library
```go
import "github.com/fibegg/sdk/fibe"
client, _ := fibe.NewClient(fibe.WithAPIKey(os.Getenv("FIBE_API_KEY")))
pg, _ := client.Playgrounds.Create(ctx, &fibe.PlaygroundCreate{Name: "demo", PlayspecID: 5})
```
A thin Go wrapper over the Fibe HTTP API with sensible defaults: automatic retry on transient failures, circuit-breaker when the upstream is sick, idempotency-key generation for safe re-tries, rate-limit awareness, structured errors. Resource "managers" (`client.Playgrounds`, `client.Agents`, `client.Tricks`, …) mirror the REST shape.
Read on: [Go library](/sdk/go-library/).
### MCP server
```sh
fibe mcp serve # stdio, single-tenant
fibe mcp serve --transport sse # SSE, multi-tenant
fibe mcp install --client claude-code
```
The same Go binary doubles as an MCP server. AI agents call `fibe_*` tools (41 of them across resource CRUD, playground actions, agent control, greenfield setup, multi-step pipelines, monitoring, repo management, and a few escape hatches). The catalog is in [Tools catalog](/sdk/tools-catalog/) and each tool has its own detail page under [Reference → Tools](/reference/tools/playgrounds-transform/).
Read on: [MCP server](/sdk/mcp-server/), [Tools catalog](/sdk/tools-catalog/).
## What's next
- [Install the CLI](/sdk/install/) — Homebrew, Go install, release binaries, Docker.
- [Authentication](/sdk/authentication/) — login flows, profiles, env vars.
- [CLI reference](/sdk/cli-reference/) — every command grouped by resource.
- [Go library](/sdk/go-library/) — embedding the SDK in your own programs.
- [MCP server](/sdk/mcp-server/) — running it for your AI agent.
- [Tools catalog](/sdk/tools-catalog/) — every MCP tool, in one place.
- [Common workflows](/sdk/workflows/) — greenfield, brownfield, pipelines, CI.
- [Troubleshooting](/sdk/troubleshooting/) — debug, common errors, schema introspection.
## Tools catalog
> https://whats.fibe.gg/sdk/tools-catalog/
Every tool the [MCP server](/sdk/mcp-server/) ships, grouped by what it operates on. Click through to a tool's detail page for parameters, return shape, examples, and edge cases.
For a deep how-to, [Common workflows](/sdk/workflows/) walks through the end-to-end stories these tools support.
Tools preserve unpaid Marquee failures as `MARQUEE_NOT_FUNDED` with HTTP status `402`. Funding and billing reads remain available.
## Auth, doctor & meta
The thin tools that bootstrap the rest. Every agent calls one or two of these at the start of a session.
| Tool | Purpose |
| --- | --- |
| [`fibe_auth_set`](/reference/tools/auth-set/) | Set credentials for this session (multi-tenant). |
| [`fibe_doctor`](/reference/tools/doctor/) | Self-diagnostic — connectivity, auth, environment sanity. |
| [`fibe_status`](/reference/tools/status/) | Account dashboard — resource counts, quotas, rate-limit headroom. |
| [`fibe_schema`](/reference/tools/schema/) | Introspect a resource's JSON schema. |
| [`fibe_help`](/reference/tools/help/) | Equivalent of `fibe ... --help` for any command. |
| [`fibe_tools_catalog`](/reference/tools/tools-catalog/) | This catalog, but as a tool the agent can call. |
| [`fibe_call`](/reference/tools/call/) | Dynamic invocation of hidden tools (escape hatch). |
| [`fibe_run`](/reference/tools/run/) | Run any CLI command (last-resort escape hatch). |
| [`fibe_update_name`](/reference/tools/update-name/) | Rename a resource. |
## Resource CRUD
The five tools that handle every resource family — Playgrounds, Tricks, Agents, Playspecs, Props, Marquees, Templates, Secrets, Webhooks, API keys, Artefacts, etc.
| Tool | Purpose |
| --- | --- |
| [`fibe_resource_list`](/reference/tools/resource-list/) | List resources of a family with filters. |
| [`fibe_resource_get`](/reference/tools/resource-get/) | Fetch one resource by ID/name. Supports file downloads (agent / artefact attachments). |
| [`fibe_resource_mutate`](/reference/tools/resource-mutate/) | Create / update / run resource-scoped operations. |
| [`fibe_resource_delete`](/reference/tools/resource-delete/) | Delete by ID/name. |
| `fibe_resource_watch` | Watch events on a resource. (Listed in the upstream catalog; no detail page yet.) |
## Playgrounds
Long-running environments — the brownfield half of the platform.
| Tool | Purpose |
| --- | --- |
| [`fibe_playgrounds_wait`](/reference/tools/playgrounds-wait/) | Block until a Playground reaches a status. |
| [`fibe_playgrounds_logs`](/reference/tools/playgrounds-logs/) | Consolidated log dump. |
| [`fibe_playgrounds_logs_follow`](/reference/tools/playgrounds-logs-follow/) | Stream live logs with progress notifications. |
| [`fibe_playgrounds_action`](/reference/tools/playgrounds-action/) | Rollout, hard-restart, stop, start, retry, maintenance on/off. Actions that use the Marquee require funding. |
| [`fibe_playgrounds_debug`](/reference/tools/playgrounds-debug/) | Comprehensive diagnostics for a stuck Playground. |
| [`fibe_playgrounds_transform`](/reference/tools/playgrounds-transform/) | One-call brownfield transform — swap template, provision repos, rollout, wait. Preserves the Playground ID. |
## Agents (Genies)
Configure, chat with, and observe AI assistants from inside another AI assistant.
| Tool | Purpose |
| --- | --- |
| [`fibe_agents_duplicate`](/reference/tools/agents-duplicate/) | Clone an agent's config with a new name. |
| [`fibe_agents_runtime_status`](/reference/tools/agents-runtime-status/) | Reachability, auth, queue depth, processing state. Live checks require a funded Marquee. |
| [`fibe_agents_send_message`](/reference/tools/agents-send-message/) | Send a chat message (with attachments). Requires a funded Marquee. |
| [`fibe_agents_start_chat`](/reference/tools/agents-start-chat/) | Start / reconnect a chat session. Requires a funded Marquee. |
| [`fibe_agents_interrupt`](/reference/tools/agents-interrupt/) | Interrupt a long-running tool call. |
| [`fibe_agents_messages`](/reference/tools/agents-messages/) | Read message history. |
| [`fibe_agents_activity`](/reference/tools/agents-activity/) | Event timeline for one agent. |
| [`fibe_agents_live_state`](/reference/tools/agents-live-state/) | Current in-flight tools, status, scratchpad. |
| [`fibe_agents_create_conversation`](/reference/tools/agents-create-conversation/) | Start a new conversation thread. |
| [`fibe_agents_delete_conversation`](/reference/tools/agents-delete-conversation/) | Tear one down. |
| [`fibe_agent_defaults_get`](/reference/tools/agent-defaults-get/) | Read your per-Player default agent config. |
| [`fibe_agent_defaults_update`](/reference/tools/agent-defaults-update/) | Write per-Player default agent config. |
| [`fibe_agent_defaults_reset`](/reference/tools/agent-defaults-reset/) | Reset defaults to platform values. |
## Greenfield: one-call setup
For "I have nothing yet; build me the whole thing".
| Tool | Purpose |
| --- | --- |
| [`fibe_launch_create`](/reference/tools/launch-create/) | Launch inline Compose, a local config file, or a GitHub repo config. |
| [`fibe_greenfield_create`](/reference/tools/greenfield-create/) | Repos → template version → Playspec → Playground → running. Single call. |
| [`fibe_templates_launch`](/reference/tools/templates-launch/) | Bootstrap a Playground from an existing import-template. |
| [`fibe_templates_search`](/reference/tools/templates-search/) | Search the template catalog. |
| [`fibe_templates_change`](/reference/tools/templates-change/) | Patch / overwrite a template, optionally rollout. |
| [`fibe_github_repos_create`](/reference/tools/github-repos-create/) | Provision a new GitHub repo (uses your GitHub App). |
| [`fibe_gitea_repos_create`](/reference/tools/gitea-repos-create/) | Same for Gitea. |
## Monitoring, mutters, feedback, artefacts
The trail your work leaves and the live event stream that surfaces it.
| Tool | Purpose |
| --- | --- |
| [`fibe_monitor_list`](/reference/tools/monitor-list/) | List recent events. |
| [`fibe_monitor_follow`](/reference/tools/monitor-follow/) | Stream events live. |
| [`fibe_mutter`](/reference/tools/mutter/) | Post a mutter (progress / evidence / blocker note). |
| [`fibe_mutters_get`](/reference/tools/mutters-get/) | Read mutters for a resource. |
| [`fibe_feedbacks_list`](/reference/tools/feedbacks-list/) | List feedback on artefacts. |
| [`fibe_feedbacks_get`](/reference/tools/feedbacks-get/) | Read one feedback. |
| [`fibe_artefact_upload`](/reference/tools/artefact-upload/) | Upload a file as an artefact attached to a resource. |
## Pipelines
The most powerful tool. Run multiple calls in sequence, parallel, or for-each, with JSONPath bindings between steps.
| Tool | Purpose |
| --- | --- |
| [`fibe_pipeline`](/reference/tools/pipeline/) | Execute a multi-step plan with bindings, parallel blocks, for-each loops, caching. |
| [`fibe_pipeline_result`](/reference/tools/pipeline-result/) | Look up a cached pipeline result (5-min TTL). |
For an example, see [Common workflows → Pipelines](/sdk/workflows/).
## Local dev
For users running a Marquee locally and wanting to peek at what's on disk.
| Tool | Purpose |
| --- | --- |
| [`fibe_local_playgrounds_info`](/reference/tools/local-playgrounds-info/) | Inspect `/opt/fibe/playgrounds/...` on the local host. |
| [`fibe_local_playgrounds_link`](/reference/tools/local-playgrounds-link/) | Mount a local Playground into the current dir. |
## Repos
Connect Fibe to source-control providers.
| Tool | Purpose |
| --- | --- |
| [`fibe_find_github_repos`](/reference/tools/find-github-repos/) | Search GitHub across your installations. |
| [`fibe_get_github_token`](/reference/tools/get-github-token/) | Mint a short-lived GitHub token. |
| [`fibe_repo_status_check`](/reference/tools/repo-status-check/) | Verify access / privacy / fork status for a repo URL. |
## Annotations
Every tool's detail page surfaces three annotations from the canonical catalog:
- **Destructive** — can permanently change or delete data. The MCP server confirms before calling these by default.
- **Idempotent** — calling twice has the same effect as calling once (good for retries).
- **Read-only** — never modifies state. Safe to call freely.
The catalog file at `/Users/vvsk/play/sdk/fibe_mcp_tools_catalog.md` is the upstream source of truth for the annotations.
## Next step
For end-to-end usage of these tools, head to [Common workflows](/sdk/workflows/) — greenfield, brownfield, pipelines, monitoring, CI integration.
## Troubleshooting
> https://whats.fibe.gg/sdk/troubleshooting/
What to do when the CLI, library, or MCP server isn't behaving.
## Step one: `fibe doctor`
```sh
fibe doctor
```
The first thing to try, always. It checks:
- Whether you can reach the Fibe API.
- Whether your active profile / `FIBE_API_KEY` is valid.
- Basic environment sanity (binary version, OS, network).
The output points at the actual problem. **If `fibe doctor` is green, the cause is in your code or in the resource you're calling against**, not in the SDK setup.
## Verbose logging
`--debug` makes the CLI dump every HTTP request, response, retry, and circuit-breaker event:
```sh
fibe --debug playgrounds create --name "x" --playspec-id 5
```
For the Go library, set the logger:
```go
client, _ := fibe.NewClient(
fibe.WithAPIKey(key),
fibe.WithDebugLogger(log.New(os.Stderr, "[fibe] ", 0)),
)
```
For the MCP server, run it with `FIBE_MCP_DEBUG=1`.
## Structured errors
`--explain-errors` makes the CLI emit a JSON error description instead of a friendly message:
```sh
fibe --explain-errors playgrounds get does-not-exist
```
The structured output includes the tool family, request ID, suggested next action, and any schema-validation details. Useful for AI agents trying to recover from errors.
## Common errors
### "no credentials"
`FIBE_API_KEY` isn't set and there's no active profile. Run `fibe login` or set the env var.
### "401 Unauthorized" / "403 Forbidden"
Your credentials are wrong, expired, or scoped too narrowly. Check:
```sh
fibe auth status # who am I supposed to be
fibe doctor # does the API agree
```
If the scope is wrong, mint a key with the right scopes in [API keys](/advanced/api-keys/).
### "429 Too Many Requests" / rate limited
The Fibe API enforces per-account rate limits. The Go library auto-respects the retry-after header; the CLI surfaces a clear message. If you hit this consistently:
- For automation, slow down or batch (use `fibe_pipeline` instead of many separate calls).
- For interactive use, wait a minute.
- If you genuinely need a higher limit, contact support.
### "circuit breaker open"
The library opens a circuit when the upstream is sustained-failing. It re-attempts periodically. If it stays open more than a few minutes, something is wrong with the API or your network. Run `fibe doctor` again.
### "validation failed: missing required field"
The resource creation or update is missing a required field. Use schema introspection to see what's needed:
```sh
fibe schema show playspec | jq '.properties | keys'
```
Or `fibe playspecs create --help` for a CLI-formatted version.
### "broken pipe" / "connection reset" mid-stream
The Playground or Trick has stopped emitting data. For `playgrounds logs --follow`, this usually means the service ended. Run `fibe playgrounds status` to confirm.
### MCP: "Authorization required" but the agent set a header
Double-check the transport (`--transport http` or `sse`, not stdio for multi-tenant). Stdio mode ignores headers and uses the host's default profile.
### MCP: tools list is empty
Likely one of:
- `FIBE_MCP_TOOLS` is set to a tier with no tools.
- The MCP server isn't running (check the client's logs).
- The client's MCP config points at the wrong binary path.
## Schema introspection
When you don't know what shape a resource takes, ask the API:
```sh
fibe schema list # all resource families
fibe schema show playground # JSON schema for one
fibe schema show playspec | yq '.properties.metadata' # drill down
```
For agents, the same is exposed as `fibe_schema`. Use it liberally before composing a `fibe_resource_mutate` call.
## Looking up a specific tool's error
Every MCP tool's detail page (see the [Tools catalog](/sdk/tools-catalog/)) documents the errors it can return. The `--explain-errors` flag on the CLI does the same.
## "Help me figure out which command to run"
```sh
fibe docs # open this site in a browser
fibe help # CLI help
fibe schema list # resource families
fibe doctor # is auth/setup OK
```
From an MCP client, the equivalent tools are `fibe_help`, `fibe_schema`, `fibe_doctor`, `fibe_tools_catalog`.
## When to file a bug
If `fibe doctor` is green, your scopes are right, and you've tried with `--debug` and a fresh `fibe login` — and the same call works through the web UI but fails via the SDK — that's a real bug. Open an issue with:
- `fibe version`
- `fibe doctor` output
- `--debug` output of the failing call (redact the API key)
- The expected vs actual behavior
## When you're stuck
Sometimes the fastest answer is a Genie. Open one of your AI assistants with access to the [reference library](/reference/intro/), give it the error message, and have it walk the relevant tool docs. The MCP server's tools (especially [`fibe_doctor`](/reference/tools/doctor/), [`fibe_status`](/reference/tools/status/), [`fibe_schema`](/reference/tools/schema/), and [`fibe_help`](/reference/tools/help/)) are designed for exactly this — they let an agent figure out the platform with you.
## Related
- [Authentication](/sdk/authentication/) — credential setup.
- [Tools catalog](/sdk/tools-catalog/) — every tool with annotations.
- [Common problems & fixes (product-side)](/operate/common-problems/) — errors that come from your template or your Playground, not the SDK.
---
# Reference: API
## Agents and knowledge API
> https://whats.fibe.gg/api/agents-and-knowledge/
import SwaggerApiReference from '@site/src/components/SwaggerApiReference';
import {agentsSections} from '@site/src/data/apiReference';
# Agents and knowledge API
These endpoints manage agent lifecycle, conversation synchronization, generated artefacts, feedback, event monitoring, conversations, and memory. Every operation below includes path/query parameters, request body examples, response summaries, and an interactive Swagger UI tester.
Agent operations that use a Marquee require that Marquee to be funded. Unpaid Marquees return `MARQUEE_NOT_FUNDED`; cached reads remain available.
## API reference
> https://whats.fibe.gg/api/
# API reference
This reference covers the public `/api` namespace exposed by Fibe. It does not cover the legacy Stripe webhook route, commented team routes, or internal-only routes outside `/api`.
Use the SDK, CLI, or MCP server when you want a supported automation surface with command discovery and auth profile handling. Use the HTTP API when you need direct REST integration.
## Base URL
Use the environment host plus the `/api` namespace:
| Environment | Base URL |
| --- | --- |
| Production | `https://fibe.gg/api` |
| Staging | `https://next.fibe.live/api` |
## Authentication
Send API keys as bearer tokens:
```http
Authorization: Bearer fibe_...
Accept: application/json
Content-Type: application/json
```
API requests are authenticated as the player that owns the token. API access is limited to beta or super-admin players. Some endpoints also require scoped API keys, such as `monitor:read` for event monitoring.
`GET /api/me` returns the current API identity and the scopes attached to the token.
## Response shapes
Most resource reads return the serialized resource directly:
```json
{
"id": 123,
"name": "example"
}
```
List endpoints use a shared envelope:
```json
{
"data": [],
"meta": {
"page": 1,
"per_page": 25,
"total": 0
}
}
```
Pagination parameters:
| Parameter | Default | Maximum | Notes |
| --- | --- | --- | --- |
| `page` | `1` | `1000` | One-based page number. |
| `per_page` | `25` | `100` | Page size. |
| `limit` | `25` | `100` | Alias used by endpoints that accept limit-style pagination. |
Validation and authorization failures use the shared error envelope:
```json
{
"error": {
"code": "validation_error",
"message": "Validation failed",
"details": {}
}
}
```
Responses include `X-Request-Id` when a request id is available.
## Async operations
Long-running operations return `202 Accepted` with a polling URL:
```json
{
"request_id": "req_...",
"status": "queued",
"status_url": "/api/async_requests/req_..."
}
```
Poll `GET /api/async_requests/:id` until the operation is terminal. Queued and running requests return `202`; terminal and error states return `200`. A missing async request returns `404`.
Some write endpoints support `Idempotency-Key` for safe retries. Reuse the same key only for retries of the same logical operation.
| Endpoint | Purpose |
| --- | --- |
| `GET /api/async_requests/:id` | Poll a queued async operation. |
## Endpoint groups
| Group | Contents |
| --- | --- |
| [Platform](./platform.mdx) | Marquees, props, playgrounds, playspecs, template imports, launches, and compose validation. |
| [Agents and knowledge](./agents-and-knowledge.mdx) | Agents, conversations, artefacts, feedback, events, memory, uploads, and conversation synchronization. |
| [Integrations](./integrations.mdx) | API keys, secrets, job environment, GitHub and Gitea repositories, installations, webhooks, and audit logs. |
Each endpoint group is rendered from an OpenAPI 3.1 definition. Click **Authorize** on a group page, paste your `FIBE_API_KEY`, choose Production or Staging, and use **Try it out** to exercise an endpoint directly from the docs.
## Integrations API
> https://whats.fibe.gg/api/integrations/
import SwaggerApiReference from '@site/src/components/SwaggerApiReference';
import {integrationsSections} from '@site/src/data/apiReference';
# Integrations API
These endpoints cover API identity, secrets, environment values, repository integrations, installation tokens, webhook endpoints, and audit logs. Every operation below includes path/query parameters, request body examples, response summaries, and an interactive Swagger UI tester.
## Platform API
> https://whats.fibe.gg/api/platform/
import SwaggerApiReference from '@site/src/components/SwaggerApiReference';
import {platformSections} from '@site/src/data/apiReference';
# Platform API
These endpoints manage Marquees, application definitions, playground lifecycle, and template import workflows. Every operation below includes path/query parameters, request body examples, response summaries, and an interactive Swagger UI tester.
Marquee operations that launch, inspect, stream, schedule, stop, destroy, or manage services require current funding. Unpaid Marquees return `MARQUEE_NOT_FUNDED` with status `402`; billing reads remain available.
---
# Reference (skills)
## Add Metadata
> https://whats.fibe.gg/reference/recipe-add-metadata/
Templates that get published (Bazaar) need metadata. Templates intended only for private launch can skip metadata, but it's still good hygiene.
## Minimum
```yaml
x-fibe.gg:
metadata:
description: "One-sentence summary of what launches when you run this template."
category: "Web"
```
That's the floor for Bazaar. Both are free-form strings; no enums.
## All recognized fields
```yaml
x-fibe.gg:
metadata:
description: "Wiki.js + Postgres production-ready stack"
category: "Productivity"
source_defaults: true
job_mode: false
schedule_config:
enabled: false
cron: "0 * * * *"
marquee_id: 1
trigger_config:
enabled: false
event_type: push
repo_url: ""
branch: main
prop_id: 1
marquee_id: 1
```
| Field | Type | Purpose |
|---|---|---|
| `description` | string | Bazaar card description and launcher summary |
| `category` | string | Bazaar filter |
| `source_defaults` | bool | Auto-fill `repo_url`/`branch` from the source Prop when imported |
| `job_mode` | bool | Marks this template as one-shot/job-mode |
| `schedule_config` | object | Cron-driven job launches |
| `trigger_config` | object | VCS-triggered job launches |
Put `job_mode`, `schedule_config`, and `trigger_config` inside `metadata` for current launch/import behavior. Root-level copies are schema-accepted compatibility mirrors, but root-only execution settings are not portable across all public flows.
## `description` style
Write **others will see** when they launch this template:
- ✅ "Wiki.js + Postgres — collaborative documentation server"
- ✅ "Ruby on Rails web stack with Postgres, Redis, and Sidekiq workers"
- ✅ "Nightly database backup to S3"
- ❌ "Web app"
- ❌ "Stack"
- ❌ "see README"
A sentence or two. No markdown.
## `category` conventions
Use a short noun phrase. Bazaar doesn't enforce a list — match the audience:
- `Web`, `Productivity`, `CI`, `Operations`, `Development`, `Database`, `AI`, `Storage`, `Communication`, `Security`.
Avoid niche categories — broader matches discovery.
## `source_defaults: true`
Set when the template will be imported from a source Prop (a Git repo). When true, the runtime auto-fills:
- `fibe.gg/repo_url` on services that have `build:`, `fibe.gg/source_mount`, or already declare `fibe.gg/repo_url`/`fibe.gg/branch` → with the source Prop's repo URL.
- `fibe.gg/branch` similarly with the source ref.
- `trigger_config.repo_url`/`branch` if the template has a `trigger_config` and `job_mode: true`.
This lets one template be used across many repos: import from any Prop, no per-import editing.
```yaml
x-fibe.gg:
metadata:
description: "CI test runner — runs `npm test` against the source repo on push"
category: "CI"
source_defaults: true
job_mode: true
trigger_config:
enabled: true
event_type: push
# repo_url and branch auto-fill from source Prop
prop_id: 1
marquee_id: 1
```
## Execution settings in metadata
Schema accepts these at both `x-fibe.gg.` and `x-fibe.gg.metadata.`. Current public import flows use the metadata location for Playspec execution settings. Reasons to keep them in metadata:
- Public templates return one metadata payload.
- Launchers and import flows can carry the template's execution shape with its description/category.
- `source_defaults` can fill trigger repo/branch in the same object that owns `trigger_config`.
If you mirror root-level fields for schema-facing tools, keep the values identical.
## Skipping metadata
Private one-off templates can skip `metadata` entirely. The template will still validate and launch. But:
- It can't be published to Bazaar (which requires `description`/`category`).
- Launchers may show "Untitled" or service-name placeholders.
## Variable-driven metadata
You can parameterize description text:
```yaml
x-fibe.gg:
metadata:
description: $$var__DESCRIPTION
category: $$var__CATEGORY
variables:
DESCRIPTION:
name: "Description"
default: "My app"
CATEGORY:
name: "Category"
default: "Web"
```
Rarely useful — these are usually fixed by the template author. But the schema allows it.
## Pitfalls
- **Markdown in `description`** — Bazaar renders as plain text. No headings, no bold.
- **Root-only `job_mode` / schedule / trigger config** — may validate but not launch/import as intended. Put execution settings in `metadata`.
- **`source_defaults: true` with NO source Prop** — runtime silently does nothing (there's no Prop to read from); explicitly declared values are still honored.
- **Forgetting `description` before publishing** — Bazaar rejects.
- **Description as the template name** — name is set separately when importing/creating the template; description is the body.
## Related skills
[reference-x-fibe-gg-namespace](reference-x-fibe-gg-namespace.md), [decide-job-mode](decide-job-mode.md), [mode-schedule-cron](mode-schedule-cron.md), [mode-trigger-vcs](mode-trigger-vcs.md), [templates-publish-checklist](templates-publish-checklist.md).
## Add Path Rule
> https://whats.fibe.gg/reference/recipe-add-path-rule/
`fibe.gg/path_rule` lets two or more services share a subdomain by partitioning HTTP traffic on the URL path. Common case: one service answers `/cable` and `/health`; another answers everything else.
## Allowed matchers
ONLY these three Traefik matchers, in any boolean combination with `&&` and `||`:
- `` Path(`/exact/url`) ``
- `` PathPrefix(`/prefix`) ``
- `` PathRegexp(`/users/[0-9]+`) ``
Schema check: value must contain `\b(?:Path|PathPrefix|PathRegexp)\s*\(`.
## Forbidden matchers
Fibe owns the **Host** rule, so these matchers are explicitly rejected by both schema and runtime:
- `Host(...)`, `HostRegexp(...)`, `HostSNI(...)`, `HostSNIRegexp(...)`
- `Headers(...)`, `HeadersRegexp(...)`
- `Method(...)`, `Query(...)`, `ClientIP(...)`
Schema check fails with: `must only contain path matchers, not Host/Headers/Method/Query/ClientIP`.
## Examples
### One specific path, one catch-all
```yaml
services:
web:
labels:
fibe.gg/expose: external:3000
fibe.gg/subdomain: app
# no fibe.gg/path_rule — catch-all on app.
api:
labels:
fibe.gg/expose: external:8080
fibe.gg/subdomain: app # same subdomain
fibe.gg/path_rule: PathPrefix(`/api`)
```
`https://app./api/...` → `api` service. Everything else → `web`.
### Multiple paths combined
```yaml
services:
ws:
labels:
fibe.gg/expose: external:8081
fibe.gg/subdomain: app
fibe.gg/path_rule: Path(`/cable`) || Path(`/health`) || PathPrefix(`/ws`)
```
This sends `/cable`, `/health`, and `/ws...` requests to the websocket service while keeping the rest of the host on the main web service.
### Path regex
```yaml
services:
legacy:
labels:
fibe.gg/expose: external:5000
fibe.gg/subdomain: app
fibe.gg/path_rule: PathRegexp(`/v[0-9]+/legacy/.*`)
```
## Backticks vs quotes in path arguments
Traefik's official syntax uses backticks around the path literal: `` Path(`/foo`) ``. Single or double quotes would be ambiguous with YAML and JSON. **Always use backticks.**
In YAML, the entire label value is a string, so backticks need no escaping:
```yaml
fibe.gg/path_rule: Path(`/cable`) || Path(`/health`)
```
If you need to template-interpolate inside the literal, mix in `$$var__NAME`:
```yaml
fibe.gg/path_rule: PathPrefix(`/$$var__PATH_PREFIX`)
```
The schema's `templatedString` rule allows variable markers anywhere in the string.
## Boolean combinations
| Combination | Meaning |
|---|---|
| `Path(`/a`) || Path(`/b`)` | matches `/a` OR `/b` |
| `Path(`/a`) && PathPrefix(`/sub`)` | matches `/a` AND URL begins with `/sub` (rare; `&&` of `Path()` makes little sense) |
| `(Path(`/a`) || Path(`/b`)) && PathPrefix(`/`) | grouped — unusual but supported by Traefik |
In practice, `||` between `Path()` / `PathPrefix()` / `PathRegexp()` is the only combination needed.
## Pattern: catch-all `web` + specific `ws`
This is the most common multi-service-per-subdomain pattern:
```yaml
services:
web: # default route
labels:
fibe.gg/expose: external:3000
fibe.gg/subdomain: $$var__SUBDOMAIN
# no path_rule
ws: # specific
labels:
fibe.gg/expose: external:8081
fibe.gg/subdomain: $$var__SUBDOMAIN
fibe.gg/path_rule: Path(`/cable`) || Path(`/health`)
```
Traefik's matchers prefer the more specific rule, so `ws` wins on `/cable` and `/health`; `web` wins elsewhere.
## Pitfalls
- **No path matcher at all** — value like `Host(`foo.bar`)` is rejected. Value MUST contain `Path`, `PathPrefix`, or `PathRegexp`.
- **Wrong quote style** — `Path('/api')` or `Path("/api")` aren't valid Traefik. Use backticks.
- **Trailing slash mismatch** — `PathPrefix(`/api`)` matches `/api` and `/api/x`. `Path(`/api`)` matches ONLY `/api`. Know which you want.
- **Trying to route by Host or Method** — Fibe owns host; the validator rejects. If you need method-based routing, do it in the app.
- **Two services with same `path_rule`** — only one wins; you'll see flapping. Make the rules disjoint.
- **Forgetting `fibe.gg/path_rule` is `req_exposed`** — required-when-exposed in the schema annotation, but the schema doesn't enforce it (presence-only requirement). Set it explicitly when sharing a subdomain.
## Related skills
[recipe-add-subdomain](recipe-add-subdomain.md), [decide-exposure-strategy](decide-exposure-strategy.md), [recipe-inline-variables](recipe-inline-variables.md), [reference-fibe-labels](reference-fibe-labels.md), [playbook-rails-app](playbook-rails-app.md).
## Add Subdomain
> https://whats.fibe.gg/reference/recipe-add-subdomain/
The public URL of an exposed service is `https://.`. The Marquee owns its `root_domain` (e.g. `next.fibe.live`); the template owns the subdomain via `fibe.gg/subdomain`.
## Allowed values
| Value | Effect | Notes |
|---|---|---|
| omitted | Default — uses **service name** as subdomain | Service `web` → `web.` |
| `` | Use `` as subdomain | Lowercase alnum + hyphens, no leading/trailing hyphen |
| `@` | Bind the route at the **root** of the Marquee | `` |
| empty string | Treated as default | Same as omitting |
| `$$var__NAME` | Variable interpolation | Resolved at launch |
| integer | Numeric string | E.g. `1234` |
Regex (schema): `^(?:|@|[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)$`. Single-character labels `a` through `z` and `0` through `9` are allowed.
## Examples
```yaml
services:
web:
labels:
fibe.gg/expose: external:3000
# no subdomain → web.
api:
labels:
fibe.gg/expose: external:8080
fibe.gg/subdomain: api # api.
docs:
labels:
fibe.gg/expose: external:80
fibe.gg/subdomain: documentation # documentation.
front_door:
labels:
fibe.gg/expose: external:3000
fibe.gg/subdomain: "@" # the root itself
```
## Variable-driven
Parameterize the subdomain at launch:
```yaml
services:
web:
labels:
fibe.gg/expose: external:3000
fibe.gg/subdomain: $$var__SUBDOMAIN
x-fibe.gg:
variables:
SUBDOMAIN:
name: "Subdomain"
required: true
default: "demo"
validation: "/^[a-z0-9][a-z0-9-]*[a-z0-9]$/"
```
Inline `$$var__SUBDOMAIN` works because the schema's `subdomainLabel` permits `templatedString` alongside the literal regex. Whole-node `path: services.web.labels.fibe.gg/subdomain` also works — see [recipe-whole-node-paths](recipe-whole-node-paths.md).
## When to use `@`
Use `@` for the **front door** — the service users hit by typing the Marquee root domain itself (no subdomain prefix). A given Marquee can only have one service at `@` (for a given path rule).
```yaml
services:
web:
labels:
fibe.gg/expose: external:3000
fibe.gg/subdomain: "@"
```
Useful when:
- The template is THE app on this Marquee.
- You want a clean public URL (`https://my-app.example.com` instead of `https://web.my-app.example.com`).
Note the quotes around `@` to keep YAML from interpreting it as a YAML directive marker.
## Sharing a subdomain with `path_rule`
Two services can share one subdomain by routing on path. The "catch-all" service should omit `fibe.gg/path_rule`; the "specific" service should set `fibe.gg/path_rule`. See [recipe-add-path-rule](recipe-add-path-rule.md).
```yaml
services:
web:
labels:
fibe.gg/expose: external:3000
fibe.gg/subdomain: $$var__SUBDOMAIN
# catch-all — no path_rule
ws:
labels:
fibe.gg/expose: external:8081
fibe.gg/subdomain: $$var__SUBDOMAIN
fibe.gg/path_rule: Path(`/cable`) || Path(`/health`)
```
Same subdomain, different paths.
## How the URL is generated (Traefik)
For an exposed service:
1. Fibe sets `traefik.enable=true` and Traefik joins `{COMPOSE_PROJECT_NAME}_default` network automatically
2. HTTP router `web` entrypoint matches `Host(`<subdomain>.<root>`)`.
3. HTTPS router `websecure` matches the same Host with ACME TLS.
4. Internal services additionally get a Basic Auth middleware with Marquee credentials.
5. Optional `fibe.gg/path_rule` ANDs into the matcher.
See [reference-fibe-labels](reference-fibe-labels.md) for the schema rules.
## Pitfalls
- **Uppercase subdomain** — `MyApp` fails the regex. Use lowercase.
- **Leading/trailing hyphen** — `-staging` or `staging-` fails. Use `staging`.
- **Underscore** — not allowed in DNS labels. Use hyphens.
- **Subdomain longer than DNS label limit** — 63 chars. Don't get clever.
- **Same subdomain on multiple services without `path_rule`** — Traefik routes only one (first match). Add `path_rule` to disambiguate.
- **`@` without quoting** — YAML may misparse. Always quote: `fibe.gg/subdomain: "@"`.
## Related skills
[recipe-add-path-rule](recipe-add-path-rule.md), [decide-exposure-strategy](decide-exposure-strategy.md), [recipe-inline-variables](recipe-inline-variables.md), [recipe-whole-node-paths](recipe-whole-node-paths.md), [reference-fibe-labels](reference-fibe-labels.md).
## Agents And Automation
> https://whats.fibe.gg/reference/fibe-agents-and-automation/
Use this skill when a user asks how Fibe's AI Genies work, how to automate work, or how jobs relate to Playgrounds.
## Agents / AI Genies
An Agent is a persistent AI assistant configuration. It stores provider choice, credentials, settings, prompts, mounted files, and runtime preferences.
Supported provider families include Gemini, Antigravity, Claude Code, OpenAI Codex, Cursor, and OpenCode. Credential type depends on provider: OAuth-style credentials, device-code credentials, pasted credential bundles, or API keys. Antigravity uses the Google OAuth code flow from its headless CLI.
An Agent is usable when authenticated and not expired or revoked.
## Agent settings
Common user-facing settings:
- Custom environment variables.
- Provider API key mode.
- Custom MCP configuration.
- Post-init script for preparing the Genie environment.
- Custom system prompt.
- Model options.
- Mounted files.
- Agent password for protected access.
- System-check toggle.
Mounted files are useful for large context, proprietary docs, scripts, fixtures, or data that should be available to a Genie without committing it to the application repository.
## Standalone chat
A standalone Genie chat runs on a selected Marquee without requiring a Playground. It gets its own protected URL and can keep conversation state. Use standalone chat when the user wants an AI workspace but does not need it attached to a specific running app.
Typical chat lifecycle:
1. User selects an authenticated Agent and target Marquee.
2. Chat starts and gets a protected URL.
3. User chats through Bridge or the Agent chat page.
4. User can extend the chat, stop it, or let it expire.
## Bridge
Bridge is the unified Genie workspace. It helps users switch among Agents and active chats, inspect reachability, open chat panes, and receive live updates from Genie work.
Use Bridge language when a user asks for "the agent hub", "all chats", "the floating agent workspace", or "switching between genies".
## Artefacts, mutters, feedback, and public activity
- Artefact: a generated output worth keeping, such as a report, screenshot, mockup, CSV, config, or interactive preview.
- Mutter: a short progress, evidence, or issue note tied to a Playground or Agent workflow.
- Feedback: user rating or review of a Genie output.
- Build in Public: optional public visibility for selected Genie activity and linked Playground context.
## Job mode and Tricks
A Trick is a job-mode Playground for tasks that should finish. It watches one or more services, waits for them to exit, records success or failure, and cleans up.
Good Trick use cases:
- Test suite run.
- Lint or build check.
- Database migration.
- Backup.
- Cleanup.
- Data sync.
- Documentation build.
- Scheduled task.
- VCS-triggered CI task.
Bad Trick use cases:
- Web app that should stay reachable.
- Background worker that should run forever.
- Development server with hot reload.
- Watch command that never exits.
## Scheduled jobs
A scheduled job is a Trick launched from a cron expression on a target Marquee.
Use scheduled jobs for repeated cleanup, backup, sync, or audit tasks. The template must be job-mode, and the watched service must exit.
## VCS-triggered jobs
A VCS-triggered job launches a Trick when a configured repository event matches, such as a push or pull request on a branch.
Use this for CI-style test runs, smoke checks, documentation builds, and automation that should react to source changes.
## Job ENV
Job ENV entries inject environment variables into Tricks. Use them for CI tokens, test service credentials, package registry tokens, and other job-only secrets.
Scopes:
- Global: applies to every Trick owned by the user.
- Prop-scoped: applies only when a Trick is tied to a specific repository.
Prefer Job ENV over template variables when a credential should be reused across many job runs and should not be supplied at every launch.
## Related skills
- [mode-job-trick](mode-job-trick.md)
- [mode-schedule-cron](mode-schedule-cron.md)
- [mode-trigger-vcs](mode-trigger-vcs.md)
- [playbook-test-runner](playbook-test-runner.md)
- [playbook-cron-scheduled](playbook-cron-scheduled.md)
- [decide-secrets-and-randoms](decide-secrets-and-randoms.md)
## Anchors And Aliases
> https://whats.fibe.gg/reference/recipe-anchors-and-aliases/
Large templates often repeat the same `environment:`, `depends_on:`, `build:`, or label blocks across services. YAML anchors deduplicate that shared shape. Fibe permits aliases, so use them when they make the template easier to read.
## Syntax recap
- `&name` — declare an anchor on a YAML node.
- `*name` — reference (alias) the anchor.
- `<<: *name` — merge keys from the anchored mapping into the current mapping.
## At the document level: `x-*` extension keys
Compose ignores top-level `x-*` keys. Anchor them there:
```yaml
x-app-environment: &app-environment
RAILS_ENV: ${RAILS_ENV:-beta}
RAILS_MASTER_KEY: ${RAILS_MASTER_KEY}
FIBE_DB_PASS: ${FIBE_DB_PASS:-password}
FIBE_DB_HOST: pgbouncer
REDIS_URL: redis://redis:6379/1
x-rails-deps: &rails-deps
setup:
condition: service_completed_successfully
pgbouncer:
condition: service_started
services:
web:
build: .
depends_on: *rails-deps
environment: *app-environment
jobs:
build: .
depends_on: *rails-deps
environment: *app-environment
```
Use this pattern for any shared dependency or environment block that appears in several services.
## Merge with `<<:`
To override or extend an anchored block:
```yaml
x-base-env: &base-env
RAILS_ENV: production
APP_NAME: my-app
services:
web:
environment:
<<: *base-env
LOG_LEVEL: info # extra key
jobs:
environment:
<<: *base-env
RAILS_ENV: development # override
```
## Anchoring a full service definition
```yaml
x-slack-notify-service: &slack-notify-service
image: node:24-slim
environment:
SLACK_WEBHOOK_URL: ${SLACK_WEBHOOK_URL:-}
restart: 'no'
command:
- /bin/sh
- -ec
- |
# ... long script ...
services:
slack-notify-awaken:
<<: *slack-notify-service
environment:
<<: *slack-notify-environment
SLACK_STATUS: is awaken...
```
Useful for "near-identical services with one or two field overrides".
## Anchoring `depends_on`
```yaml
x-setup-dependencies: &setup-dependencies
<<: *slack-notify-awaken-dependency
postgres:
condition: service_healthy
pgbouncer:
condition: service_started
redis:
condition: service_healthy
services:
setup:
depends_on: *setup-dependencies
jobs:
depends_on:
<<: *setup-dependencies
setup:
condition: service_completed_successfully
```
## Anchors and `$$var__`
`$$var__NAME` is string substitution, so it works inside anchored values too:
```yaml
x-app-environment: &app-environment
APP_NAME: $$var__APP_NAME
DATABASE_URL: postgres://postgres:$$var__DB_PASSWORD@db:5432/app
services:
web:
environment: *app-environment
worker:
environment: *app-environment
```
When `$$var__APP_NAME` is substituted, both services get the same value.
## Anchors and `path:` bindings
`path:` writes to the **compiled** YAML structure. Aliases are resolved during YAML load; the editor walks the structure once. Writing to `services.web.environment.APP_NAME` does NOT propagate to the aliased other service — they were already separate nodes after the alias expansion.
If you want the same value in two services via a path binding, list both paths:
```yaml
x-fibe.gg:
variables:
APP_NAME:
name: "App name"
paths:
- services.web.environment.APP_NAME
- services.worker.environment.APP_NAME
```
Or use inline `$$var__NAME` inside an anchored block — that propagates because substitution runs on the raw template text before aliases expand.
## Anchors and label keys
You can anchor a labels block too:
```yaml
x-fibe-app-labels: &fibe-app-labels
fibe.gg/repo_url: $$var__REPO_URL
fibe.gg/branch: $$var__BRANCH
fibe.gg/dockerfile: Dockerfile
fibe.gg/source_mount: /app
services:
web:
labels:
<<: *fibe-app-labels
fibe.gg/expose: external:3000
fibe.gg/subdomain: $$var__SUBDOMAIN
jobs:
labels:
<<: *fibe-app-labels
fibe.gg/start_command: bin/jobs
```
Use this when several roles build from the same repository but expose different commands or routes.
## Limits
- Anchors and aliases are **YAML-level** — they're expanded by the parser before any Fibe processing.
- The schema validates the expanded form. If your anchor produces invalid labels, validation fails at that service after expansion.
- Some YAML tools strip aliases on round-trip — if you reformat with a custom tool, verify aliases survive.
## Pitfalls
- **Anchor used before declared** — YAML 1.2 requires the anchor to appear in document order before its alias. Place `x-*` blocks at the top of the file.
- **Aliases inside `x-fibe.gg.variables` definitions** — works, but the runtime treats variable definitions as a flat hash. Anchors there are over-engineered.
- **Cycles** — YAML aliases cannot loop. `<<: *foo` inside the `&foo`-anchored block is invalid.
- **Stripping aliases for "readability"** — fine, but the template gets longer. Choose taste over DRY only when the duplication is small.
## Related skills
[recipe-extract-env-variables](recipe-extract-env-variables.md), [recipe-inline-variables](recipe-inline-variables.md), [playbook-multi-service](playbook-multi-service.md), [playbook-rails-app](playbook-rails-app.md).
## Build Args And Target
> https://whats.fibe.gg/reference/recipe-build-args-and-target/
Map Compose `build.args` / `build.target` into Fibe labels. The labels are string-typed; multiple args are flattened into one comma-separated string.
## `fibe.gg/build_target`
A single string — the name of the Dockerfile stage to build:
```yaml
labels:
fibe.gg/build_target: production
```
The Dockerfile must contain a matching `FROM ... AS production` stage. If unset, the final stage is built (default Docker behavior).
## `fibe.gg/build_args`
A **comma-separated string** of `KEY=value` pairs:
```yaml
labels:
fibe.gg/build_args: "RAILS_ENV=production,NODE_VERSION=20,RUBY_VERSION=3.3"
```
Parsing rules:
- Split on `,`.
- For each part, split on the first `=`.
- Whitespace around key and value is trimmed.
- Empty keys are skipped.
- Empty values are kept as empty strings.
So `"K1=,K2=value,, K3 = v3 "` parses to `{ K1: "", K2: "value", K3: "v3" }`.
## Variable interpolation
Values can contain `$$var__NAME`:
```yaml
labels:
fibe.gg/build_args: "NODE_VERSION=$$var__NODE_VERSION,RAILS_ENV=$$var__RAILS_ENV"
fibe.gg/build_target: $$var__BUILD_TARGET
```
The whole label value can also be a variable (less common; usually you want a hardcoded list):
```yaml
labels:
fibe.gg/build_args: $$var__BUILD_ARGS
x-fibe.gg:
variables:
BUILD_ARGS:
name: "Build args (KEY=value,...)"
default: "NODE_VERSION=20"
```
## Mapping from Compose
```yaml
# BEFORE (Compose)
services:
web:
build:
context: .
dockerfile: Dockerfile
target: production
args:
RAILS_ENV: production
NODE_VERSION: "20"
DEBIAN_FRONTEND: noninteractive
# AFTER (Fibe)
services:
web:
labels:
fibe.gg/repo_url: https://github.com/owner/repo
fibe.gg/dockerfile: Dockerfile
fibe.gg/build_target: production
fibe.gg/build_args: "RAILS_ENV=production,NODE_VERSION=20,DEBIAN_FRONTEND=noninteractive"
```
## When values contain commas
The simple comma split breaks if a value itself contains a comma. There is no escape syntax. Avoid commas in build arg values; encode out-of-band if you need them (base64, URL encoding, etc.).
```yaml
# BAD — comma in value confuses parser
fibe.gg/build_args: "ALLOWED_HOSTS=a.com,b.com,LANG=en_US.UTF-8"
# GOOD — split, or encode
fibe.gg/build_args: "ALLOWED_HOSTS_B64=YS5jb20sYi5jb20=,LANG=en_US.UTF-8"
```
## When values contain `=`
The first `=` is the separator; subsequent `=` go into the value. So `K=foo=bar` parses as `{ K: "foo=bar" }`. Safe.
## Confirming the build args are passed
After launching, you can verify the args were honored by inspecting the build log via `fibe_playgrounds_logs` or `fibe_playgrounds_debug`. Check that the build command shows `--build-arg KEY=VALUE` lines for each pair.
## Pitfalls
- **Forgetting to quote** the value — most build arg strings won't trip YAML, but `RAILS_ENV: production` (with space) inside YAML needs care. Use quoted form to be explicit:
```yaml
fibe.gg/build_args: "RAILS_ENV=production,NODE_VERSION=20"
```
- **Comma in values** — parser splits, you lose data. Encode or split into multiple args.
- **Wrong stage name** — typo in `fibe.gg/build_target` → build fails at the docker-build step with a "stage not found" error. Read `Dockerfile` to confirm.
- **Build args used but not declared in Dockerfile** — Docker silently ignores. `ARG NAME` must appear in the Dockerfile stage.
- **Multi-stage Dockerfile with shared args** — each stage that uses an arg needs its own `ARG NAME` directive. Docker doesn't propagate.
- **Trying to override `target` via env var** — there is no `fibe.gg/target_env`; use `$$var__BUILD_TARGET` interpolation.
## Related skills
[recipe-build-to-repo-url](recipe-build-to-repo-url.md), [recipe-source-mount](recipe-source-mount.md), [recipe-inline-variables](recipe-inline-variables.md), [decide-static-vs-dynamic](decide-static-vs-dynamic.md), [reference-fibe-labels](reference-fibe-labels.md).
## Build To Repo Url
> https://whats.fibe.gg/reference/recipe-build-to-repo-url/
When the input Compose has a `build:` block, the service is built from source. Fibe's runtime owns clone + build. Drop most of `build:` and lift the relevant fields into `fibe.gg/*` labels.
## Mapping
| Compose `build:` field | Fibe label / behavior |
|---|---|
| `build:` exists | **Add** `fibe.gg/repo_url: ` (REQUIRED — schema validator rejects build without it) |
| `build: .` or `context: .` | Runtime build context becomes the cloned repo path |
| `context: subdir/` | Not preserved as the runtime context — use `fibe.gg/dockerfile: subdir/Dockerfile` and ensure the Dockerfile works from the repo root, or restructure |
| `dockerfile: Dockerfile.dev` | `fibe.gg/dockerfile: Dockerfile.dev` |
| `target: production` | `fibe.gg/build_target: production` |
| `args: { KEY: value, K2: v2 }` | `fibe.gg/build_args: "KEY=value,K2=v2"` (comma-separated string) |
After conversion, the `build:` block can be **deleted entirely**. Keeping it marks the service as a build workflow, but runtime generation replaces the build context with the cloned repo path and uses the Fibe labels for Dockerfile/target/args.
## Step-by-step
1. **Identify the repo** (`https://github.com/owner/repo` or your Gitea instance — `https://...`).
2. **Add `fibe.gg/repo_url`** with that URL. If the launcher should choose, use `$$var__REPO_URL` and declare the variable.
3. **Pin Dockerfile** if it isn't at repo root: `fibe.gg/dockerfile: `.
4. **Pin branch** if you don't want the default: `fibe.gg/branch: `. Default branch is the repo default.
5. **Move build target** if multi-stage: `fibe.gg/build_target: `.
6. **Move build args** to comma-separated string: `fibe.gg/build_args: "K=v,..."`.
7. **Delete the `build:` block.**
8. **Add `image:` for the base image you want as a placeholder while waiting for first build** (optional). Helpful for dev mode where the source-mount runs against this image. The image field on dynamic services is treated as the base; Fibe replaces it once a built image is available.
## Before / after
### Simple `build:` → labels
```yaml
# BEFORE
services:
web:
build: .
ports:
- "3000:3000"
environment:
RAILS_ENV: development
# AFTER
services:
web:
image: ruby:3.3 # base image, optional
labels:
fibe.gg/repo_url: https://github.com/owner/repo
fibe.gg/expose: external:3000
fibe.gg/production: "false"
fibe.gg/start_command: bin/rails server -b 0.0.0.0
environment:
RAILS_ENV: development
```
### Multi-stage build with args
```yaml
# BEFORE
services:
api:
build:
context: .
dockerfile: deploy/Dockerfile
target: production
args:
NODE_VERSION: "20"
BUILD_ENV: production
# AFTER
services:
api:
labels:
fibe.gg/repo_url: https://github.com/owner/repo
fibe.gg/dockerfile: deploy/Dockerfile
fibe.gg/build_target: production
fibe.gg/build_args: "NODE_VERSION=20,BUILD_ENV=production"
fibe.gg/expose: external:8080
fibe.gg/production: "true"
```
### Launch-time configurable
```yaml
services:
web:
labels:
fibe.gg/repo_url: $$var__REPO_URL
fibe.gg/branch: $$var__BRANCH
fibe.gg/dockerfile: Dockerfile
fibe.gg/expose: external:3000
x-fibe.gg:
variables:
REPO_URL:
name: "Repository URL"
required: true
default: "https://github.com/owner/repo"
BRANCH:
name: "Branch"
required: true
default: "main"
```
Use this pattern when the launcher should choose the repository and branch.
## Subdirectory builds (monorepo-style)
If the project is in a monorepo and the Dockerfile assumes a subdirectory context, restructure or adjust `COPY` paths for a repo-root build context. The `fibe.gg/dockerfile` value can point into a subdirectory (`apps/web/Dockerfile`), but the Dockerfile must be self-sufficient relative to the cloned repo root.
## `image:` on dynamic services
Compose allows both `image:` and `build:`. With Fibe:
- In `production: "false"` (dev) mode, `image:` is what runs initially while source is mounted into `fibe.gg/source_mount`. Pick something with the language runtime: `node:24-slim`, `python:3.12`, `ruby:3.3`, `golang:1.23`. Avoid `:latest`.
- In `production: "true"` mode, `image:` is a placeholder; Fibe builds a fresh image from the Dockerfile and replaces it.
## Pitfalls
- **Forgetting `fibe.gg/repo_url`** — schema/runtime hard error: `Service '' has a build directive but lacks a fibe.gg/repo_url label`.
- **Using a non-HTTPS repo URL** — use `https://github.com/...` or the HTTPS URL for a connected built-in repository. SSH URLs (`git@github.com:owner/repo.git`) fail.
- **Pointing Dockerfile outside the repo** — paths are interpreted relative to the cloned repo root.
- **Trying to build from a local directory** — not supported. Fibe is not `docker compose build`; the source must be a remote VCS URL the platform can clone.
## Related skills
[recipe-build-args-and-target](recipe-build-args-and-target.md), [recipe-source-mount](recipe-source-mount.md), [recipe-strip-incompatible-keys](recipe-strip-incompatible-keys.md), [decide-static-vs-dynamic](decide-static-vs-dynamic.md), [playbook-rails-app](playbook-rails-app.md), [playbook-nodejs-dev](playbook-nodejs-dev.md), [reference-fibe-labels](reference-fibe-labels.md).
## Common Errors And Fixes
> https://whats.fibe.gg/reference/common-errors-and-fixes/
A short lookup of frequent validation and launch errors with the exact fix. The error text below is the substring Fibe usually returns through public validation, preview, launch, CLI, or MCP flows.
## Hidden runtime inference (no hard error, but behavior changes)
Some signals change behavior even when no explicit error is raised:
- **Job-mode lifecycle is enforced**
If `x-fibe.gg.metadata.job_mode: true` is enabled, Fibe applies:
- `restart: "no"` on all services
- `deploy.replicas: 1` on all services
If you intended a long-running app, remove job-mode.
- **`hostname:` is removed from compiled compose**
Hostnames are stripped at compile time by platform-level routing rules.
- **Variable precedence is deterministic**
Inline interpolation applies first, then `path`/`paths` rewrites. If both target the same value, path rewrite wins.
- **Source mode is inferred from structure**
`build:` and `fibe.gg/source_mount` both require `fibe.gg/repo_url`. Missing either fails launch/preview.
## Schema and label-parser errors
### `Service '': unknown label ''`
You wrote a label under `fibe.gg/` that isn't in the whitelist. The 19 supported labels are listed in [reference-fibe-labels](reference-fibe-labels.md).
**Fix:** Remove the unknown label, or rename to a supported one. Non-`fibe.gg/` labels (`traefik.enable: "true"`, `com.example.owner`) pass through.
### `Service '' has a build directive but lacks a fibe.gg/repo_url label`
Compose `build:` requires `fibe.gg/repo_url`.
**Fix:** Add `fibe.gg/repo_url: ` to the service's `labels`. See [recipe-build-to-repo-url](recipe-build-to-repo-url.md).
### `Service '' has source_mount but no repo_url`
`fibe.gg/source_mount` requires `fibe.gg/repo_url`.
**Fix:** Either add `fibe.gg/repo_url`, or remove `fibe.gg/source_mount`. See [recipe-source-mount](recipe-source-mount.md).
### `Service '': zerodowntime services must have 'fibe.gg/expose' set`
Zero-downtime requires an exposed HTTP service.
**Fix:** Add `fibe.gg/expose: external:PORT` (or `internal:PORT`). See [decide-zero-downtime](decide-zero-downtime.md).
### `Service '': zerodowntime services cannot have 'ports'`
Compose `ports:` is incompatible with rolling updates.
**Fix:** Remove `ports:`. The service is reachable via `fibe.gg/expose`. See [recipe-ports-to-expose](recipe-ports-to-expose.md), [recipe-strip-incompatible-keys](recipe-strip-incompatible-keys.md).
### `Service '': zerodowntime services cannot have 'container_name'`
Container names must be unique; replicas duplicate them.
**Fix:** Remove `container_name:`. See [recipe-strip-incompatible-keys](recipe-strip-incompatible-keys.md).
### `Service '': invalid repo_url '' — must be a valid GitHub or Gitea repository URL`
The URL isn't HTTPS / isn't a supported provider.
**Fix:** Use `https://github.com/owner/repo` or your Gitea host. SSH URLs (`git@...`) fail. See [recipe-build-to-repo-url](recipe-build-to-repo-url.md).
### `Service '': invalid exposure visibility '' — must be 'internal' or 'external'`
Only `internal:PORT` / `external:PORT` / bare `PORT`.
**Fix:** Use lowercase `internal` or `external`. `External:3000` fails — case-sensitive. See [recipe-ports-to-expose](recipe-ports-to-expose.md).
### `Service '': invalid exposure port '' — must be a number between 1 and 65535`
Port out of range.
**Fix:** Use a real port number.
### `Service '': invalid subdomain ''`
Subdomain regex: `^[a-z0-9]([a-z0-9-]*[a-z0-9])?$`, or `@`, or empty.
**Fix:** Lowercase, no leading/trailing hyphen, no underscore. See [recipe-add-subdomain](recipe-add-subdomain.md).
### `Service '': invalid path_rule '' — must contain a valid Traefik path matcher`
`path_rule` must contain at least one of `Path(`, `PathPrefix(`, `PathRegexp(`.
**Fix:** Add a path matcher. See [recipe-add-path-rule](recipe-add-path-rule.md).
### `Service '': invalid path_rule '' — must only contain path matchers, not Host/Headers/Method/Query/ClientIP`
Forbidden matchers (Host/HostRegexp/HostSNI/HostSNIRegexp/Headers/HeadersRegexp/Method/Query/ClientIP) appear. Fibe owns Host rules.
**Fix:** Remove the forbidden matchers. See [recipe-add-path-rule](recipe-add-path-rule.md).
### `Service '': invalid healthcheck_interval '' — must be a duration`
Duration must match `^[0-9]+(ms|s|m)$`. Not `h`, not `d`.
**Fix:** Use `30s`, `1m`, `500ms`. Convert larger units (`60s` not `1m` is also valid).
### `Service '': invalid healthcheck_retries '' — must be a positive integer`
Healthcheck retries: `^[1-9][0-9]*$`. Not `0`. Not `-1`.
**Fix:** `"3"`, `"12"`, etc.
### `Service '': invalid