A command-line tool for managing Fusebase applications. Build, develop, and deploy apps to the Fusebase platform.
See Architecture for detailed documentation on the CLI's architecture, command system, configuration, and development workflow.
For deeper understanding of:
- MCP integration
- API client layers (legacy vs SDK)
- LLM capability discovery
- Core concepts (products, apps, data access)
See:
- Architecture Documentation
- CLI Flows
- Git Configuration Guide
- Conceptual Model
- App Permissions — canonical model for
dashboardView,database,gate, andapp update - Fusebase Gate meta (
fusebaseGateMeta) — Gate SDK analyze flow andfusebase.jsonsnapshot - E2E tests — CLI end-to-end smoke + dev-start parallel tests, env vars, and CI variables
See CLI Commands & Interactive Prompts for comprehensive documentation on all available commands, options, and interactive prompts.
Prerequisite: Bun v1.0+ — the CLI runs as a Bun script and requires Bun at runtime regardless of how you install it.
Install Bun if you don't have it:
curl -fsSL https://bun.sh/install | bash
The CLI is not published to npm. Install globally from this repo:
Option 1 – Install from Git:
bun install -g git+https://github.com/fusebase-dev/fusebase-cli.gitBun downloads the package and links the fusebase binary globally.
Option 2 – Link from a local clone (for development):
cd /path/to/apps-cli
bun install
npm linkThen run fusebase from anywhere. Use npm link, not bun link --global — Bun does not add the package bin to your PATH.
Run without installing:
cd /path/to/apps-cli
bun index.ts [command]Print CLI version (from package.json).
Set the API key for authentication with the Fusebase API.
Options:
| Option | Description |
|---|---|
--api-key <apiKey> |
The API key to store. If not provided, the OAuth auth flow is started. |
--dev |
Use the dev environment |
Example:
# Interactive mode
fusebase auth
# Direct mode
fusebase auth --api-key=your-api-key-here
# Use dev environment
fusebase auth --api-key=your-api-key --devInitialize a new Fusebase app in the current directory. This command will:
- Prompt you to select an organization (or use the only one if there's just one)
- Let you select an existing app or create a new one
- Optionally copy a project template if the directory is empty
- Create a
fusebase.jsonconfiguration file
Arguments: None
Options:
--name <name>- App title/name (if not provided, prompted)--subdomain <subdomain>- App subdomain (e.g.my-app)--org <orgId>- Organization ID (skips org selection)--ide <preset>- IDE preset:claude-code,cursor,vscode,opencode,codex, orother(single choice; generates all IDE configs by default)--force- Overwrite existing IDE config files/folders--git- After setup, initialize local Git and sync with configured GitLab remote (creates/uses repo in<gitlabGroup>/<dev|prod>/..., setsorigin, pushes current branch)- Also enabled automatically if global flag
git-initis active (fusebase config set-flag git-init)
- Also enabled automatically if global flag
--skip-git- Skip local Git initialization and GitLab sync (overrides both--gitand globalgit-init)--git-tag-managed- If app is managed, addmanagedtopic to the GitLab project during sync- In interactive init, CLI shows a suggested GitLab repo name and lets you edit it before sync
Interactive Prompts:
- Organization selection - Choose from your available organizations
- App selection - Choose an existing app or create a new one
- When creating a new app: enter title and subdomain
- Project template - If directory is empty, the template is used automatically. If not empty, you'll be asked whether to continue in the current folder.
- IDE configuration - MCP config is generated for all supported IDEs by default (unless
--ideis provided); required MCP servers from the catalog (respecting flags) are written automatically. Optional servers are not configured during init — runfusebase integrationslater. - App name - Name for
package.json(if using template)
Example:
mkdir my-app
cd my-app
fusebase initOutput:
Creates a fusebase.json file with the following structure:
{
"orgId": "your-org-id",
"productId": "your-app-id"
}Initialize a local Git repository in the current directory (git init), ensure baseline .gitignore, and print local workflow hints.
Sync the current local repository with GitLab using global config from ~/.fusebase/config.json:
gitlabHost(for examplegl.nimbusweb.co)gitlabTokengitlabGroup(base namespace; env subgroupdev/prodis selected from current auth env)
Behavior:
- Creates/uses GitLab project with visibility
private - Project name is generated as
app-<base>-<env>(for exampleapp-workspace-tools-dev) - Base priority: Fusebase app title (with transliteration fallback for Cyrillic) → current folder name → app
subdomain - Configures local
origin(without overwriting existing different origin) - Pushes current branch to remote
- With
--git-tag-managed, applies topicmanagedfor managed apps - Equivalent short form:
fusebase git --git-sync [--git-tag-managed]
Config example:
{
"gitlabHost": "gl.nimbusweb.co",
"gitlabToken": "glpat-xxxxxxxxxxxxxxxx",
"gitlabGroup": "vibecode"
}Examples:
cd my-app
fusebase git
fusebase git sync
fusebase git sync --git-tag-managedList all apps for the current app with their URLs.
Arguments: None
Options: None
Prerequisites:
- App must be initialized (
fusebase init) - API key must be configured (
fusebase auth)
Example:
fusebase app listOutput:
Apps:
My App
ID: app-id-123
URL: https://your-app-id.thefusebase.app/my-app
Permissions:
ID Title Type
--------------- ---------------- --------
dashboard-id-123 Sales Dashboard Table
database-id-456 Customer Database Database
Total: 1 app(s)
Deploy apps to Fusebase. For each app this command will:
- Install dependencies and run lint (if the app has a
lintscript in itspackage.json) - Run the build command (if configured)
- Compute a SHA-256
frontendHashof the upload directory and abackendHashof thebackend/folder (if present) - Compare those hashes against the active version and take one of:
- No changes → skip the app entirely (no new version, no upload, no backend deploy). Logs
✓ No changes for app, skipping deploy. - Frontend unchanged, backend changed → create a new version, reuse the previous frontend bundle via
copyFrontendParams(no upload), then re-deploy the backend. - Frontend changed → create a new version, upload files, persist the new
frontendHash. Backend is handled per its own hash (skipped/copied or re-deployed).
- No changes → skip the app entirely (no new version, no upload, no backend deploy). Logs
- With
--force, hash matches are ignored and a full upload + redeploy runs for every app. - If the app contains
openapi.jsonand theapp-api-registryflag is enabled, validate it and publish the app API manifest to the app registry.
Arguments: None
Options:
| Option | Description |
|---|---|
--force |
Force re-upload and re-deploy regardless of frontend/backend hash match |
Prerequisites:
- App must be initialized (
fusebase init) - API key must be configured (
fusebase auth) - At least one app must have a
pathconfigured infusebase.json
Examples:
# Skips apps with unchanged frontend + backend
fusebase deploy
# Always uploads and redeploys
fusebase deploy --forceApp Configuration in fusebase.json:
{
"orgId": "...",
"productId": "...",
"apps": [
{
"id": "app-id",
"path": "apps/my-app",
"build": {
"command": "npm run build",
"outputDir": "dist"
}
}
]
}Validate the app OpenAPI contract for the Phase 1 app API MVP.
Behavior:
- looks for
openapi.jsonin the current directory by default - also detects
openapi.yaml/openapi.yml, but YAML validation is not supported in this MVP yet - validates:
OpenAPI 3.1info.titleinfo.version- operation presence
- unique
operationId - basic
x-fusebase-*fields
Examples:
fusebase api validate
fusebase api validate --file openapi.jsonOutput:
- success summary with title, version, and operation ids
- or a list of validation issues with JSON paths
Update settings for an existing app.
Arguments:
| Argument | Required | Description |
|---|---|---|
appId |
Yes | The ID of the app to update |
Options:
| Option | Description |
|---|---|
--access <principals> |
Set access principals, comma-separated (e.g., visitor, orgRole:member) |
--permissions <permissions> |
Replace dashboardView/database permissions |
--sync-gate-permissions |
Analyze the app path and replace gate permissions |
Access Principals:
The --access option replaces the entire access principal list. Principals are comma-separated entries:
| Principal | Description |
|---|---|
visitor |
Any unauthenticated visitor (public access) |
orgRole:<id> |
Org members with a specific role. Valid ids: guest, client, member, manager, owner |
Examples:
# Make an app publicly accessible
fusebase app update feat_abc123 --access=visitor
# Allow org members only
fusebase app update feat_abc123 --access=orgRole:member
# Allow multiple roles
fusebase app update feat_abc123 --access=orgRole:member,orgRole:client
# Public access + org members
fusebase app update feat_abc123 --access=visitor,orgRole:member
# Replace dashboard/database permissions only
fusebase app update feat_abc123 --permissions="dashboardView.dash_1:view_1.read;database.id:db_1.write"
# Sync Gate permissions only
fusebase app update feat_abc123 --sync-gate-permissions
# Replace dashboard/database and Gate permissions in one request
fusebase app update feat_abc123 --permissions="dashboardView.dash_1:view_1.read" --sync-gate-permissionsSee App Permissions for the full permissions model and merge semantics.
Create and configure an app for development.
Options:
| Option | Description |
|---|---|
--name <name> |
(Required) App title |
--subdomain <subdomain> |
(Required) Subdomain for the app (e.g., my-app) |
--path <path> |
(Required) Local app directory path (e.g., apps/my-app) |
--dev-command <command> |
(Required) Dev server command (e.g., npm run dev) |
--build-command <command> |
(Required) Build command (e.g., npm run build) |
--output-dir <dir> |
(Required) Build output directory (e.g., dist) |
--access <principals> |
Set access principals on creation (e.g., visitor, orgRole:member) |
--permissions <permissions> |
Set manual dashboardView/database permissions |
Example:
fusebase app create --name="My App" --subdomain=my-app --path=apps/my-app --dev-command="npm run dev" --build-command="npm run build" --output-dir=distIf you later scaffold a backend into that app with:
fusebase scaffold --template backend --dir apps/my-appthe CLI creates openapi.json automatically if it does not already exist.
The spa feature template ships a built-in /link route that handles app magic links: it reads ?id=…&redirect=… from the URL, calls the Gate activation endpoint (POST /apps/magic-links/{id}/activate), persists the returned tokens as cookies (fbsfeaturetoken for the Gate feature token, fbsdashboardtoken for the dashboard-service feature token, eversessionid for the session), and redirects to the deep page. Expired, revoked, and not-found cases each render a tailored UI inline, so closed-access apps work for one-click client onboarding without extra wiring. The Gate base URL is auto-derived from window.location.hostname for Fusebase-managed app domains; for custom domains set VITE_FUSEBASE_GATE_URL=https://<your-gate-host>/v4/api/proxy/gate-service/v1 at build time (Vite inlines import.meta.env.VITE_* into the bundle). See project-template/.claude/skills/fusebase-gate/references/app-magic-links.md for the full Gate contract (invite vs self-service vs activate).
Updates fusebase.json:
{
"orgId": "...",
"productId": "...",
"apps": [
{
"id": "app-id",
"path": "apps/my-app",
"dev": {
"command": "npm run dev"
},
"build": {
"command": "npm run build",
"outputDir": "dist"
}
}
]
}Start the development server for an app. This command:
- Starts the app's dev server (if
dev.commandis configured) - Starts the Fusebase dev server UI (port 4173)
- Starts the API proxy server (port 4174)
- Creates a per-session debug log folder under the selected app directory at
logs/dev-<timestamp>/ - Opens the dev UI in your browser
Arguments:
| Argument | Required | Description |
|---|---|---|
app |
No | App ID or path (from fusebase.json apps). If not provided, you'll be prompted to select one. |
Options: None
Prerequisites:
- App must be initialized (
fusebase init) - API key must be configured (
fusebase auth) - At least one app must be configured in
fusebase.json
Example:
# Interactive app selection
fusebase dev start
# Start specific app by ID
fusebase dev start my-app-id
# Start specific app by path
fusebase dev start apps/dashboardDev Server Components:
| Component | Port | Description |
|---|---|---|
| Frontend UI | 4173 | React app that displays apps in iframes |
| API Proxy | 4174 | Proxies requests to Fusebase API with authentication |
Per-session Debug Logs:
Each fusebase dev start run creates a session folder inside the selected app directory:
<app-dir>/logs/dev-<timestamp>/
browser-logs.jsonl
access-logs.jsonl
backend-logs.jsonl
frontend-dev-server-logs.jsonl
App Token Flow:
The dev server automatically handles app token delivery:
- Fetches app tokens from the Fusebase API
- Sends tokens to the app iframe via
postMessage - Sets cookie
fbsapptokenso same-origin app backend requests can authenticate without relying on a custom header - Your app receives the token:
window.addEventListener('message', (event) => {
if (event.data?.type === 'featuretoken' && event.data?.token) {
// Use event.data.token for API calls
}
});For custom app backends (/api/*), treat x-app-feature-token as optional in deployed mode and read x-app-feature-token or cookie fbsapptoken on the server.
One command to refresh a generated app after a CLI or template upgrade:
- CLI binary update — runs first (skips automatically in local linked/source mode). Use
--skip-cli-updateto disable this stage. On Windows, this launches the installer and exits sofusebase.execan be replaced; after the installer finishes, runfusebase updateagain to continue app stages. - Agent assets — refreshes
AGENTS.md,.claude/skills/,.claude/agents/,.claude/hooks/,.claude/settings.json. - MCP + IDE — selectively regenerates Dashboards and/or Gate MCP tokens and refreshes IDE configs when the CLI’s permission policy no longer matches
.envmarkersDASHBOARDS_MCP_POLICY_FPandGATE_MCP_POLICY_FP(SHA-256 of the canonical permission sets; Gate includesisolated-storesextras when that global flag is on). Tokens must also be present in.env. Use--force-mcpto refresh both regardless. - Managed SDK versions — bumps only packages listed under
fusebaseCli.managedDependenciesinproject-template/package.json(defaults to@fusebase/dashboard-service-sdkand@fusebase/fusebase-gate-sdk). Rootpackage.jsongets missing entries added; apppackage.jsonfiles are updated only if those deps already exist (nothing new is injected into apps). npm install— runs only in directories where a managed dependency version actually changed.
Pre-update Git checkpoint: In a TTY, you are prompted for an optional commit before changes (empty commit if the tree is clean). If current branch tracks a remote (upstream configured), the pre-update commit is pushed immediately. Without Git, you are warned about rollback risk and can initialize a repo first. Use --skip-commit to skip, or --commit to run the checkpoint in CI/non-interactive mode without prompts.
Prerequisites: fusebase.json with orgId and productId; fusebase auth for stages that touch MCP tokens.
Behavior by directory:
- In an app directory (
fusebase.jsonexists): runs full flow (CLI + app stages). On Windows, a CLI binary update exits after launching the installer; rerun the command after installation to run app stages. - Outside an app directory: runs only CLI binary update.
- Use
--skip-productto force CLI-only mode even inside an app directory.
Examples:
fusebase update
fusebase update --dry-run
fusebase update --skip-product
fusebase update --skip-skills --force-mcp
fusebase update --skip-install
fusebase update --skip-commitFlags (stages default on; use no-* to disable):
| Flag | Effect |
|---|---|
--skip-product |
Skip app stages and run only CLI update |
--skip-cli-update |
Skip automatic CLI self-update stage |
--skip-skills |
Skip agent asset refresh |
--skip-mcp |
Skip MCP token + IDE refresh |
--force-mcp |
Always refresh MCP tokens + IDE configs |
--skip-deps |
Skip managed dependency version sync |
--skip-install |
After dep sync, do not run npm install |
--skip-commit |
Skip pre-update Git checkpoint |
--commit |
Run Git checkpoint without prompts (non-interactive) |
--dry-run |
Print planned work only |
fusebase update is the single update command.
Manage sidecar containers for an app backend or for a specific cron job. Sidecars are pre-built Docker images that run alongside the main container, sharing its network namespace (reachable on localhost). Stored in fusebase.json under apps[].backend.sidecars[] (backend) or apps[].backend.jobs[].sidecars[] (per cron job).
# Add a sidecar to the backend (default — same as today)
fusebase sidecar add --feature <featureId> --name <name> --image <image> \
[--port <port>] [--tier small|medium|large] [--env KEY=VALUE ...] \
[--secret KEY|KEY:ALIAS ...]
# Add a sidecar to a specific cron job (requires the job-sidecars flag)
fusebase sidecar add --feature <featureId> --job <jobName> --name <name> --image <image> \
[--port <port>] [--tier small|medium|large] [--env KEY=VALUE ...] \
[--secret KEY|KEY:ALIAS ...]
# Remove a sidecar
fusebase sidecar remove --feature <featureId> --name <name> [--job <jobName>]
# List configured sidecars
fusebase sidecar list --feature <featureId> [--job <jobName>]Options:
--feature <featureId>(required) — app ID--name <name>(required for add/remove) — sidecar name. Lowercase letters, digits, and hyphens; max 63 chars; must start with a lowercase letter.--image <image>(required for add) — Docker image reference (e.g.browserless/chrome:latest)--port <port>— port the sidecar listens on (informational;localhost:<port>from the main container)--tier small|medium|large— resource tier (default:small)--env KEY=VALUE— environment variables, repeatable--secret KEY|KEY:ALIAS— whitelist an app secret key (registered viafusebase secret create) to inject into the sidecar as an env var, repeatable. UseKEY:ALIASto expose the secret under a different env var name inside the sidecar. On collision between sidecarenvand a secret key, the sidecar's staticenvvalue wins. Deploy fails with aValidationErrorlisting every missing key if any referenced secret is not registered for the app.--job <jobName>— attach the sidecar to the named cron job instead of the backend. Requires thejob-sidecarsflag (fusebase config set-flag job-sidecars). Without--job, all three subcommands target backend sidecars exactly as today.
Limits and rules:
- Max 3 sidecars per scope. The backend's cap is independent of each job's cap — every job has its own 3-sidecar budget.
- Sidecar names must be unique within a scope. The same name (e.g.
chromium) may exist on the backend and on a cron job; they are separate containers in separate replicas. - Backend sidecars share the backend container app's network namespace. Cron-job sidecars share the cron job replica's network namespace only — they are isolated from the backend's sidecars and from sidecars in other jobs.
- Replicas of a cron job complete when the main job container exits. Non-exiting sidecars (headless browsers, Redis, etc.) are torn down automatically with the replica;
replicaTimeout=3600sis the hard ceiling. fusebase dev startdoes not run cron jobs nor sidecars — they only take effect afterfusebase deploy.
Create or overwrite .env in the current app with MCP token and URL. Use this after fusebase init or when the token has expired.
When .env is created/updated, the command refreshes both Dashboards and Gate MCP tokens. In interactive terminals, it then offers to immediately run fusebase config ide --force for all IDE MCP configs; if declined, it prints that command as the next step.
Options: --no-force — only create .env if missing; do not overwrite existing file.
Prerequisites: App must be initialized (fusebase.json with orgId), API key configured (fusebase auth).
Example:
fusebase env createGlobal configuration stored in your home directory:
{
"apiKey": "your-api-key",
"env": "dev",
"flags": ["mcp-beta"],
"gitlabHost": "gl.nimbusweb.co",
"gitlabGroup": "vibecode",
"gitlabToken": "glpat-xxxxxxxxxxxxxxxx"
}Flags gate experimental features. The update command uses flags to conditionally include/exclude template assets via Eta templates.
| Flag | Effect |
|---|---|
mcp-beta |
Unlocks optional MCP servers in the integrations catalog that are marked beta (see ide-configs/mcp-servers.ts) |
git-init |
Makes fusebase init automatically offer local Git initialization (same behavior as passing --git; can be disabled per run with --skip-git) and includes Git workflow skill files in generated apps |
git-debug-commits |
Enables strict debug/deploy traceability section inside the git-workflow skill: deploy preflight + dirty-tree guard, commit-per-fix, and SHA/tag traceability in debug/deploy reports |
app-business-docs |
Copies the app-business-docs skill into the app: keeps docs/en/business-logic.md (English) aligned with real behavior — domain rules, main user flows, edge cases; update after business-logic changes or when debugging unclear behavior |
mcp-gate-debug |
Copies the mcp-gate-debug skill: after Fusebase Gate MCP tool runs, summarize smooth vs rough paths and suggest improvements to .claude/skills/fusebase-gate, prompts, or MCP server behavior — prioritize isolated stores (SQL/NoSQL) flows |
isolated-stores |
Enables isolated stores functionality (SQL/NoSQL); also turns on required template references and isolated_store.* permissions in fusebase env create |
portal-specific-apps |
Includes portal-specific app guidance in prompts: fusebase-portal-specific-apps skill, {{CurrentPortal}} dashboard filter reference, and portal auth-context handling notes |
job-sidecars |
Enables per-job sidecar containers for cron jobs. Unlocks --job <jobName> on fusebase sidecar add/remove/list so sidecars can be attached to specific cron jobs (apps[].backend.jobs[].sidecars[]) in addition to the backend. Each job has its own 3-sidecar cap, independent of the backend cap; sidecar names are unique per scope. Also gates the per-job sidecar sections of the app-sidecar and app-backend skill templates. |
Enable a flag globally, then refresh the project template:
fusebase config set-flag app-business-docs # Business-logic documentation skill
fusebase config set-flag mcp-gate-debug # Gate MCP debug / improvement summary skill
fusebase config set-flag isolated-stores # Isolated stores functionality (SQL/NoSQL)
fusebase config set-flag portal-specific-apps # Portal-specific apps prompts/guidance
fusebase update --skip-mcp --skip-deps --skip-cli-update --skip-commit # Refresh agent assets onlyOther examples:
fusebase config set-flag mcp-beta # Enable beta-gated MCP catalog entries
fusebase config remove-flag mcp-beta # Disable
fusebase config flags # Interactive flag selector (TTY)
fusebase config flags --list # List active flags (non-interactive)
fusebase update --skip-mcp --skip-deps --skip-cli-update --skip-commit # Regenerate project filesTo permanently graduate a flag (remove gating and enable the feature forever), use the /remove-flag skill in your coding agent:
/remove-flag <flag-name>
Re-run IDE MCP setup in the current project (same logic as during fusebase init): copy config for the chosen IDE and substitute URL/token from .env.
fusebase config ide # Generate MCP config for all IDEs
fusebase config ide --ide cursor # Use Cursor preset
fusebase config ide --ide cursor --force # Overwrite existing filesConfigure the GitLab settings used by fusebase init --git and fusebase git sync:
fusebase config gitlab # Interactive setup/update
fusebase config gitlab --show # Print current values (token masked)
fusebase config gitlab --host gl.nimbusweb.co --group vibecode --token glpat_xxx
fusebase config gitlab --clear-token # Remove stored tokenInteractive catalog (optional servers) plus custom HTTP MCP servers stored in fusebase.json under mcpIntegrations.custom:
fusebase integrations # checkbox: catalog optional + custom entries
fusebase integrations --ide cursor # limit writes to one IDE (optional)
fusebase integrations --no-prompt # skip UI; optional catalog = inferred from IDE configs
fusebase integrations list-templates # requires managed-integrations flag
fusebase integrations connect-template --template-name github # requires managed-integrations flag; scopes to current appId
# Custom server (GET reachability check by default; use --skip-check to skip)
fusebase integrations add my-mcp --url https://example.com/mcp --type http [--token TOKEN]
fusebase integrations add my-mcp --url https://example.com/mcp --header 'Authorization: Bearer x'
fusebase integrations disable my-mcp # keep fusebase.json entry; strip from IDE configs
fusebase integrations enable my-mcp # turn back on and re-apply IDE configs
fusebase integrations remove my-mcp # alias: delete — remove from fusebase.json and IDE configsCustom definitions may include token (sent as Authorization: Bearer … unless you set headers yourself) and enabled: false when disabled.
Project-specific configuration in your app root:
{
"orgId": "organization-id",
"productId": "app-id",
"apps": [
{
"id": "app-uuid",
"path": "apps/my-app",
"dev": {
"command": "npm run dev"
},
"build": {
"command": "npm run build",
"outputDir": "dist"
}
}
]
}-
Authenticate with your API key:
fusebase auth
-
Initialize a new app:
mkdir my-app && cd my-app fusebase init
-
Configure an app for development:
fusebase app create
-
Start the development server:
fusebase dev start
-
Deploy to Fusebase:
fusebase deploy
The CLI automatically detects common frameworks and suggests appropriate dev/build commands:
- Vite -
npm run dev/npm run build(output:dist) - Next.js -
npm run dev/npm run build(output:.next) - Create React App -
npm start/npm run build(output:build) - Generic npm - Reads from
package.jsonscripts
| Variable | Description |
|---|---|
ENV |
Set to dev to use the development environment |
MIT