Create a brand
A brand is a directory with a brand.json manifest and its artwork. Every branding
command reads the brand from, in order:
--brand <directory>onbrand:check,brand:prepareandbrand:stage,- the
FROGG_BRAND_DIRenvironment variable, brands/frogg(stock frogg).
Relative paths resolve against the repository root.
1. Scaffold the brand
Section titled “1. Scaffold the brand”Prepare a square icon, PNG or SVG, at least 1024 × 1024 pixels. An SVG needs
width="1024" height="1024" (or larger) on its root element. Use a transparent
background unless the mark should sit on a solid tile.
npm run brand:init -- \ --dir brands/acme \ --id acme \ --name "Acme Studio" \ --app-id com.acme.studio \ --port 10099 \ --icon ~/Downloads/acme-icon.svgBrand created at /home/you/acme-studio/brands/acme. Build with FROGG_BRAND_DIR=/home/you/acme-studio/brands/acmeAll six flags are required. brand:init copies the icon into the directory as
icon.svg or icon.png, writes a minimal manifest, and validates it. It refuses to
overwrite an existing directory.
{ "schemaVersion": 1, "id": "acme", "name": "Acme Studio", "applicationId": "com.acme.studio", "daemonPort": 10099, "assets": { "icon": "./icon.svg" }}That manifest already builds a working product. Everything else has a default.
Choosing the required values
Section titled “Choosing the required values”id: a lowercase slug (acme,acme-studio). It becomes the CLI name, home directory, env prefix, URL scheme and service names unless you override them. Treat it as permanent once you ship: changing it moves users’ state to a new directory.applicationId: reverse DNS with at least three segments, lowercase letters and digits only (com.acme.studio). It is the Android package, macOS bundle ID, Windows install path and browser storage namespace. Also permanent.daemonPort: 1024 to 65535. Pick a port no other frogg-based product uses, so both can run on one machine. Stock frogg uses 9999; the example uses 10099.
2. Add colours and details
Section titled “2. Add colours and details”Add the optional fields you need. A typical product manifest:
{ "schemaVersion": 1, "id": "acme", "name": "Acme Studio", "fullName": "Acme Studio for Engineering", "publisher": "Acme Corp", "description": "Acme Studio: run and monitor AI coding agents.", "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" } }, "links": { "website": "https://studio.acme.example", "docs": "https://studio.acme.example/docs", "support": "https://github.com/acme-corp/frogg/issues" }, "distribution": { "repository": "acme-corp/frogg", "updates": "github-release" }}Use the Rebrand tool to start from an example, upload your logo,
preview Workspace, Welcome and Companion pages, and configure your brand.
It checks the manifest, reserved identities, logo dimensions and both palettes’
text contrast before exporting a ZIP with brand.json and the uploaded assets.
Unzip it into brands/<your-id>/, then continue with the check and build steps below.
Colour pairs must reach a 4.5:1 contrast ratio: accent against
accentForeground, and background against foreground, in both appearances. Omit
colors to get a neutral grey palette. See the
manifest reference for every field.
Keep secrets out of this directory. Everything in brand.json is public: it is
embedded in the app, installers and container labels.
3. Check it
Section titled “3. Check it”npm run brand:check -- --brand brands/acmeAcme Studio: valid (com.acme.studio); updates github-releaseAdd --json to print the fully resolved brand, including every derived default:
npm run --silent brand:check -- --brand brands/acme --json{ "id": "acme", "name": "Acme Studio", "cliName": "acme", "desktopBinaryName": "acme-desktop", "homeDir": ".acme", "envPrefix": "ACME", "scheme": "acme", "serviceName": "acme-daemon", "launchdLabel": "com.acme.studio-daemon", "artifactPrefix": "acme", "storagePrefix": "com.acme.studio:", "...": "..."}brand:check fails on:
- unknown keys or an unsupported
schemaVersion, - invalid slugs, application IDs or ports,
- a missing icon, or one that is not square and at least 1024 pixels,
- colour pairs under 4.5:1 contrast,
updatesenabled withoutdistribution.repository,- reserved frogg identities (see troubleshooting).
4. Prepare generated inputs
Section titled “4. Prepare generated inputs”FROGG_BRAND_DIR=brands/acme npm run brand:prepareBrand prepared: Acme Studio (3f9c1a0b52de)You rarely run this by hand. npm ci, typecheck and every dev:* and build:*
entrypoint prepare the selected brand first. The hash in brackets is the
fingerprint of the manifest, artwork, generator, bundled skills, installer
templates and app version. An unchanged fingerprint skips asset generation.
Generated output is git-ignored. Never commit it.
| Path | Contents |
|---|---|
.generated/branding/brand.json |
Fully resolved brand, read by Electron, release scripts and CI |
.generated/branding/icons/ |
icon.ico, icon.icns, PNG sizes for desktop packaging |
.generated/branding/scripts/ |
Branded install.sh, uninstall.sh, install-docker.sh, uninstall-docker.sh |
.generated/branding/skills/ |
Bundled agent skills, renamed to <id>-<skill> for custom brands |
.generated/branding/deploy/ |
compose.json, pair-worker.json, install-worker.json, runtime.sh |
.generated/branding/eas.json |
EAS config with your App Store ID, used by brand:eas |
.generated/branding/provenance.json |
Brand ID, version, source revision and fingerprint |
apps/ui/.generated/branding/ |
Expo icons, splash, favicons, PWA manifest and index.html |
packages/branding/src/generated/ |
The resolved brand compiled into the runtime package |
5. Stage for Docker and Nix
Section titled “5. Stage for Docker and Nix”Docker build contexts and Nix derivations cannot read a brand directory outside the
source tree. brand:stage copies the manifest and artwork into a self-contained,
git-ignored directory and prints its path:
FROGG_BRAND_DIR=../acme-brand npm run --silent brand:stage.branding-input/acmeThe Docker and release scripts call this for you. Stock frogg prints brands/frogg.
6. Run it locally
Section titled “6. Run it locally”Select the brand for the whole shell session, then use the normal dev commands:
export FROGG_BRAND_DIR=brands/acmenpm run dev:server # terminal 1: branded daemonnpm run dev:app # terminal 2: Expo web and mobile dev servernpm run dev:desktop # terminal 3 (optional): Electron shell$env:FROGG_BRAND_DIR = 'brands/acme'npm run dev:winThe dev scripts go through scripts/dev/branded-run.mts, which prepares the brand and
holds it for the life of the process.
| Setting | Stock frogg | Custom brand |
|---|---|---|
| Daemon listen | 0.0.0.0:6768 |
0.0.0.0:<daemonPort> (Acme: 10099) |
| Metro / Expo | 8081 | daemonPort + 1 (Acme: 10100) |
| Dev state | .dev/frogg-home |
.dev/<id>-home (Acme: .dev/acme-home) |
Override with FROGG_LISTEN, EXPO_PORT or <PREFIX>_HOME (ACME_HOME) when you run
several checkouts of the same product.
7. Build locally
Section titled “7. Build locally”export FROGG_BRAND_DIR=brands/acmenpm run build:ui # web export in apps/ui/distnpm run build:desktop -- --target linux-x64 # Electron packages in apps/desktop-electron/releasenpm run build:server && npm run build:daemon-web-uinpm run build:daemon-bundle -- --target linux-x64 --out-dir dist/bundlesDesktop targets are linux-x64, linux-arm64, win-x64, win-arm64, darwin-x64
and darwin-arm64. macOS packages must be built on macOS. Daemon bundle targets are
the same six; the daemon bundle includes Node, the daemon, the web UI and a CLI
launcher named after your cliName.