Skip to content

CLI

The muhkoo CLI drives the platform from your terminal — create apps, provision their backend (tables, agents, functions), deploy hosted clients, and manage domains, keys, and releases. It’s built on @muhkoo/connect, so it speaks the same APIs your app does.

npm install -g @muhkoo/cli

Requires Node.js 20+. Verify with muhkoo --version.

Two ways to authenticate; both store the resulting session token in ~/.muhkoo/config.json.

Terminal window
muhkoo login --web

Opens auth.muhkoo.dev in your browser, where you sign in however you like — password, passkey, or Google. The CLI captures the session over a one-time localhost redirect, so your credentials never touch the terminal. Best when you use passkeys or Google sign-in.

Every command can also override the stored session with --token <sessionToken> or the MUHKOO_DEV_TOKEN environment variable — handy in CI, where you paste a token from the portal instead of logging in.

A provision spec is a JSON file describing your backend. The CLI applies it idempotently — re-running updates tables, agents, and functions in place.

app.json
{
"slug": "team-standup",
"allowedOrigins": "*",
"email": "you@example.com", // only needed to bootstrap a new developer
"crossOriginIsolation": true, // optional — serve the app cross-origin-isolated (SharedArrayBuffer / threaded wasm). See Hosting.
"tables": [
{
"table": "tasks",
"columns": [
{ "name": "title", "type": "text", "nullable": false },
{ "name": "done", "type": "boolean", "nullable": false, "default": false }
]
}
],
"agents": [ { "handle": "@helper", "model": "", "systemPrompt": "", "enableChannel": "general" } ],
"functions": [ { "name": "hello", "code": "export default { async fetch() { return new Response('hi'); } }" } ]
}
Terminal window
muhkoo provision --spec app.json

This creates the app (issuing test + live key pairs), provisions the tables, and creates any agents and functions. It writes .muhkoo-app.json — the app id and keys — so later commands run with no extra flags.

Terminal window
muhkoo provision --spec app.json --dry-run # preview the API calls, change nothing
muhkoo provision --spec app.json --enable # enable agents/functions on their channels

A function’s code runs server-side and may need a secret (an API token, a signing key). Don’t commit it. Keep a committed app.template.json with placeholders, and generate the real app.json at provision time by substituting values from your environment — so the secret lives in CI/your shell, never in git:

app.template.json
{
"slug": "team-standup",
"functions": [
{ "name": "notify", "code": "export default { async fetch() { /* uses __SLACK_TOKEN__ */ } }" }
]
}
Terminal window
# Substitute, provision, then discard the generated spec.
sed "s/__SLACK_TOKEN__/$SLACK_TOKEN/" app.template.json > app.json
muhkoo provision --spec app.json

Because provision is idempotent, re-running it after editing the template updates tables, agents, and functions in place — it’s safe to run on every change.

Build your client, then ship its dist/ to Muhkoo hosting. Uploads are content-addressed — only changed files transfer — and a release is an atomic pointer flip (instant, and rollback-able).

Terminal window
muhkoo deploy
# → Deploying 18 files from dist/ → https://api.muhkoo.dev
# 12 uploaded, 6 unchanged (18 unique files)
# ✓ Deployed. live at: https://team-standup.apps.muhkoo.dev

deploy reads the app id and a secret key from .muhkoo-app.json. In CI, pass them explicitly:

Terminal window
muhkoo deploy --app "$MUHKOO_APP_ID" --key "$MUHKOO_DEPLOY_KEY"

Every app has a test and a production environment, and the deploy lands in whichever the secret key belongs to. muhkoo deploy uses the test key from .muhkoo-app.json by default; pass a live secret key to ship production:

Terminal window
muhkoo deploy # → test (<slug>.test.apps.muhkoo.dev)
muhkoo deploy --key "$MUHKOO_LIVE_SK" # → production (<slug>.apps.muhkoo.dev)

Iterate against test, then promote test → production — in place, as an atomic pointer flip — with muhkoo promote (or from the portal):

Terminal window
muhkoo promote # copy the test release + functions onto prod

promote is owner-only (it uses your developer session, not an app key) and reads the app id from .muhkoo-app.json. Production data and per-env app config (CORS + redirect URIs) are never touched. See Environments & deploy.

deploy is built for CI: it authenticates with an app secret key (no interactive login) and only transfers changed files. Build your client, then run muhkoo deploy with the app id and a deploy key from your CI secrets — a live key to ship production, a test key for a preview environment:

.github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: npm ci && npm run build
- run: npx @muhkoo/cli deploy
env:
MUHKOO_APP_ID: ${{ secrets.MUHKOO_APP_ID }}
MUHKOO_DEPLOY_KEY: ${{ secrets.MUHKOO_LIVE_SK }} # an app secret key

To provision the backend in CI instead (or as well), authenticate with a session token from the portal via MUHKOO_DEV_TOKEN, and run muhkoo provision --spec app.json.

--base selects the API base for any command — prod (default), staging, local, or a literal URL. muhkoo login remembers your choice.

Terminal window
muhkoo apps ls --base staging
muhkoo deploy --base local # http://localhost:8787
muhkoo whoami --base https://api.example.com
Terminal window
muhkoo apps ls # your apps
muhkoo apps get <appId> # details + key metadata + hosting URL
muhkoo apps create --slug my-app --email you@x # create (bootstraps billing on first app)
muhkoo apps slug my-app # is a slug available?
muhkoo keys rotate <appId> # rotate all four keys (revokes the old set)
muhkoo deploy # publish dist/ to the test env
muhkoo promote # promote test → production, in place
muhkoo hosting status <appId> # releases + current pointer
muhkoo hosting rollback <appId> --release <id> # instant rollback
muhkoo hosting unpublish <appId> # remove the live pointer
muhkoo domains add <appId> app.example.com # attach a custom domain (returns DNS records)
muhkoo domains ls <appId>
muhkoo tables ls <appId>
muhkoo agents ls <appId> · agents enable <appId> <agentId> --channel general
muhkoo functions ls <appId> · functions code <appId> <functionId>
muhkoo logs <appId> # inspect the server-log Space

Add --json to most read commands for machine-readable output. Run muhkoo <command> --help for the full option list on any command.

muhkoo eject shows the system prompt and tools config a @Muhkoo*-decorated agent description compiles to — what the agent will actually be told and allowed to do — before you provision it.

Terminal window
muhkoo eject src/agent/agentApp.ts

Most flags have an environment-variable equivalent, so CI never needs interactive input:

VariableEquivalent flagPurpose
MUHKOO_DEV_TOKEN--tokenDeveloper session token — for provision and account/app management in CI
MUHKOO_DEPLOY_KEY--keyApp secret key (mk_*_sk_*) — authorizes deploy
MUHKOO_APP_ID--appThe app id, when not running from a provisioned app dir
MUHKOO_API_BASE--baseAPI base: prod (default), staging, local, or a literal URL

A provisioned app directory carries its own .muhkoo-app.json (app id + keys), so locally these are usually unnecessary — they exist mainly for CI and scripting.

A server, CI job or script authenticates with an access token rather than a user sign-in — scoped to what it needs, and expiring.

Terminal window
muhkoo tokens create <appId> --scopes db:read,db:write --expires-in 30
muhkoo tokens ls <appId>
muhkoo tokens revoke <appId> <keyId>

The plaintext is shown once, at creation. See access tokens for the scope vocabulary.

muhkoo vfs is client.vfs from the terminal — the same code the SDK runs, not a reimplementation over HTTP.

Terminal window
muhkoo vfs ls / # first run pairs this machine
muhkoo vfs cd /apps/my-app
muhkoo vfs cat src/App.tsx
muhkoo vfs find '**/*.ts'
muhkoo vfs history src/App.tsx
muhkoo vfs restore src/App.tsx 0

cd is remembered between commands, so relative paths work the way they do in a shell.

The first command that needs your files asks for your password once, then pairs this machine: a random key is generated here, your master seed is wrapped under it, and only the ciphertext goes to your vault. The seed itself never lands on disk.

Terminal window
muhkoo devices ls # machines paired to your account
muhkoo devices rm <id> # withdraw one machine's access, from anywhere

Revoking works because the key here is useless without the ciphertext there — which is exactly what a stored seed could never offer.

Storing bytes is metered, and the shard store attributes them to an app. So reads work with nothing signed in, and writes need an app:

Terminal window
muhkoo vfs use-app my-app.apps.muhkoo.dev

The domain is enough when the app publishes /.well-known/muhkoo.json. Key plaintexts are never stored server-side — they are issued once at apps create — so for an app that publishes nothing, pass --key the first time and the slug thereafter.

Terminal window
muhkoo vfs mount ~/muhkoo --path /apps/my-app

Pulls the subtree to a local directory and keeps both ends in step until you stop it: edits on disk go up, changes made anywhere else come down over the live feed. Every decision is made from a content hash rather than a timestamp, which is what stops a write echoing back around the loop.

Deletes propagate — but never when the other side changed after the delete, since honouring a stale delete would destroy newer work. If both sides changed, your local copy wins and the other version stays in muhkoo vfs history.

It runs in the foreground; .git, node_modules and dist are ignored.

AreaCommands
Accountlogin · logout · whoami · devices ls|rm
Appsapps ls|get|create|slug|rm · keys rotate · tokens ls|create|revoke
Backendprovision · tables · agents ls|get|rm|enable|disable|models · functions ls|get|code|deploy|rm|enable|disable
Hostingdeploy · hosting status|rollback|rm-release|unpublish · domains ls|add|rm
Filesvfs cd|pwd|ls|stat|tree|cat|put|get|mkdir|rm|cp|mv|find · vfs history|restore · vfs mount · vfs use-app · vfs sweep
Toolslogs · eject