Skip to content

Worked example: Acme Studio

This walkthrough ships Acme Studio from a fork at acme-corp/frogg. It uses brands/example, which is already in the repository, so every command here works on a fresh clone. Output shown is real output from this repository at 0.6.19.

brands/example/brand.json
{
"schemaVersion": 1,
"id": "acme",
"name": "Acme Studio",
"applicationId": "com.acme.studio",
"daemonPort": 10099,
"assets": { "icon": "./icon.svg" },
"colors": {
"light": { "accent": "#6d28d9", "accentForeground": "#ffffff", "background": "#faf5ff", "foreground": "#27143d" },
"dark": { "accent": "#c4b5fd", "accentForeground": "#27143d", "background": "#20152e", "foreground": "#faf5ff" }
}
}

icon.svg is a 1024 × 1024 purple tile with a white “A”. There is no distribution block, so updates are disabled, and no services, so there is no hosted pairing page or relay.

  1. Fork frogg-app/frogg to acme-corp on GitHub and enable Actions in the fork.

  2. Clone and add upstream:

    Terminal window
    git clone https://github.com/acme-corp/frogg.git acme-studio && cd acme-studio
    git remote add upstream https://github.com/frogg-app/frogg.git
    npm ci
  3. Copy the example into a directory you own, so later upstream changes to the example never touch your product:

    Terminal window
    cp -r brands/example brands/acme
  4. Check it:

    Terminal window
    npm run --silent brand:check -- --brand brands/acme
    Acme Studio: valid (com.acme.studio); updates disabled

Add distribution details so releases, updates and installers use acme-corp/frogg:

brands/acme/brand.json (added)
"publisher": "Acme Corp",
"links": {
"website": "https://studio.acme.example",
"support": "https://github.com/acme-corp/frogg/issues"
},
"distribution": {
"repository": "acme-corp/frogg",
"updates": "github-release"
}
Terminal window
npm run --silent brand:check -- --brand brands/acme
Acme Studio: valid (com.acme.studio); updates github-release

Commit the brand on main:

Terminal window
git add brands/acme
git commit -m "Add Acme Studio brand"
git push origin main
Terminal window
export FROGG_BRAND_DIR=brands/acme
npm run dev:server # terminal 1: listens on 0.0.0.0:10099, state in .dev/acme-home
npm run dev:app # terminal 2: Expo on 10100

Open http://<your-host>:10100 in a browser, or run npm run dev:desktop for the Electron shell. The window, icon, favicon, accent colour and product name are Acme’s.

To try the production shape instead (daemon serving its own web UI):

Terminal window
export FROGG_BRAND_DIR=brands/acme
npm run build:server && npm run build:daemon-web-ui
ACME_HOME=/tmp/acme-home node apps/cli/bin/frogg start --listen 0.0.0.0:10099 --web-ui

The CLI built from this checkout is branded, even though the in-repo launcher file is still called frogg:

$ node apps/cli/bin/frogg --help
Usage: acme [options] [command]
Acme Studio CLI - control your AI coding agents from the command line
$ node apps/cli/bin/frogg status
KEY VALUE
Local Daemon running
Acme Studio Home /tmp/acme-home
Listen 0.0.0.0:10099
Relay disabled
...

Acme and frogg can run on the same machine. Their identities do not overlap:

Resource frogg Acme Studio
CLI frogg acme
State ~/.frogg ~/.acme
Env override FROGG_HOME ACME_HOME
Port 9999 10099
systemd unit frogg-daemon acme-daemon
launchd label app.frogg.frogg-daemon com.acme.studio-daemon
URL scheme frogg:// acme://
App ID app.frogg.frogg com.acme.studio
Desktop binary frogg acme (Electron executable uses the brand id)
Skills installed for agents frogg, frogg-dev, … acme-frogg, acme-frogg-dev, …

Branding CI runs both daemons side by side to check this (scripts/ci/branding-runtime-isolation.mjs).

Terminal window
gh variable set FROGG_BRAND_DIR --body brands/acme --repo acme-corp/frogg
# Android release signing (do this before the first release)
keytool -genkeypair -v -keystore acme-release.keystore -alias acme -keyalg RSA -keysize 4096 -validity 10000
gh secret set FROGG_ANDROID_KEYSTORE_BASE64 --body "$(base64 -w0 acme-release.keystore)" --repo acme-corp/frogg
gh secret set FROGG_ANDROID_KEYSTORE_PASSWORD --repo acme-corp/frogg
gh secret set FROGG_ANDROID_KEY_ALIAS --body acme --repo acme-corp/frogg
# Optional: Windows signing, Docker Hub
gh secret set AZURE_TENANT_ID --repo acme-corp/frogg
# ... AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, TRUSTED_SIGNING_ENDPOINT, TRUSTED_SIGNING_ACCOUNT, TRUSTED_SIGNING_PROFILE

Try a test build before tagging anything:

Terminal window
gh workflow run build-selected.yml --repo acme-corp/frogg -f target=all
gh run list --repo acme-corp/frogg --workflow build-selected.yml --limit 1

The run uploads windows-x64-client-<sha>, linux-x64-daemon-<sha> and android-arm64-debug-signed-<sha> artifacts named with the acme prefix.

Terminal window
npm run version:all:beta:patch # 0.6.19 -> 0.6.20-beta.1, commits the bump

Add a changelog section, amend it into the release commit, then tag and push:

CHANGELOG.md
## 0.6.20-beta.1 — 2026-09-20
- First Acme Studio beta, based on frogg 0.6.19.
Terminal window
git add CHANGELOG.md && git commit --amend --no-edit
git tag -d v0.6.20-beta.1 2>/dev/null # npm version tagged the pre-amend commit
npm run release:push # tags HEAD as v0.6.20-beta.1 and pushes

The Release workflow creates a draft Acme Studio 0.6.20-beta.1 prerelease, builds every platform, uploads acme-... assets and the generated installers, then publishes.

Install the daemon from it on a test host, and the desktop app from the release page:

Terminal window
curl -fsSL https://github.com/acme-corp/frogg/releases/download/v0.6.20-beta.1/install.sh | bash
acme status

Ship a second beta the same way and confirm the desktop app (on the beta channel) and acme update --channel beta both move to it. Then release stable with npm run version:all:promote and npm run release:push.