Skip to main content

CLI and Configuration Deployment

The CLI creates, validates, deploys, promotes, and rolls back immutable ChattyBox configuration versions. Each deployment token is scoped to exactly one existing project and selected environments. Treat one config file plus one project-scoped token as one managed ChattyBox project.

Release and Runtime Status

The public registry provides @openstaticfish/chattybox-cli@0.3.1, @openstaticfish/chattybox-config@0.3.1, and the separate JavaScript SDK @openstaticfish/chattybox@0.1.4. The published CLI/config packages implement the 0.3.0 backend/config contract; local and backend validation have parity and reject unknown keys. Package versions and schemaVersion: "1" are separate versioning systems.

The starter config and basic commands below exist in the published CLI. The expanded configuration reference is supported by the published 0.3.1 CLI/config packages and the matching 0.3.0 backend contract. The bundled validator covers project, discovery/scheduling fields, expanded locale controls, widget.icon, and widget.showBranding; local and backend validation reject unknown keys.

The CLI is an ESM command-line program declaring Node.js 20 or newer. bunx respects its Node shebang by default, so have Node installed, or explicitly select Bun with bunx --bun @openstaticfish/chattybox-cli .... It is not browser tooling. Published-consumer CI covers Node 20, 22, and 24 on Linux, not a general OS or browser compatibility matrix. Pin a tested package version in production automation; the unversioned examples resolve the installed/cached or registry package, not this repository's source.

What Config as Code Manages

The config controls the project's name and description, assistant persona and fallback behavior, website discovery and refresh policy, locale behavior, and hosted-widget presentation. A preview or production deployment applies those values and queues an in-place corpus refresh. With Config-as-code lock enabled, config-owned settings are changed through versioned deployments rather than dashboard forms, with a deliberate exception for the entitlement-controlled branding preference.

Initial project creation is currently a one-time dashboard step. Credentials and operational data stay outside configuration: public widget keys and optional origin restrictions, deployment-token lifecycle, analytics/conversations/content gaps, destructive actions, and uploaded icon files are not config fields. Use a URL or emoji for a declarative custom icon. The config intentionally contains no project ID: the secret deployment token selects the one project that receives it.

CredentialScopeSafe in browser code?
Public widget API keyChat and widget configurationYes; works across browser origins by default. Optional exact origin restrictions are defense in depth.
Config deployment tokenConfig deploy/promote/rollback/status for one project and selected environment labelsNo; keep in local/CI secrets

Create a Configuration

bunx @openstaticfish/chattybox-cli init

This creates chattybox.config.json:

{
"schemaVersion": "1",
"assistant": {
"name": "Support"
},
"knowledge": {
"sources": [
{
"type": "website",
"url": "https://example.com"
}
]
},
"widget": {
"enabled": true
}
}

Use a custom path when the config belongs in a subdirectory:

bunx @openstaticfish/chattybox-cli init config/chattybox.config.json

Validate Locally or in CI

bunx @openstaticfish/chattybox-cli validate
bunx @openstaticfish/chattybox-cli validate config/chattybox.config.json

Current-source validation checks object shapes, the required assistant name, enums, HTTP(S) URLs, integer limits, locale combinations, and six-digit hex colors. knowledge may be omitted for local validation or development, but when present must contain exactly one website source. Preview and production require that source. widget.enabled is required if widget is present.

Validation reads JSON only; it does not load TypeScript, JSONC, YAML, or a remote schema. It makes no network request, checks no token or plan entitlement, and does not apply defaults. Local and backend validation both reject unknown properties. A local success is not a deployment guarantee.

Schema v1 rejects unknown fields at every object level, including misspellings and arbitrary extensions. Include/exclude entries must be non-empty strings. runtime.locale is shorthand for fixed locale with overrides disabled and cannot be combined with localeMode, defaultLocale, or allowLocaleOverride: true. Local and management deployment validation share the same rules; plan entitlements and token scope are checked by the server.

Create a Deployment Token

Open the project Settings tab, find Config deployment tokens, and create a token for the environments your workflow may change. Copy it immediately; ChattyBox stores only its hash and cannot show it again.

The form initially selects development only and no expiry. Choose preview or production explicitly when needed. The dashboard offers no expiry or 7, 30, 90, or 365 days; the authenticated creation API accepts an integer from 1 to 365 days, or omission for no expiry. A project may have at most 20 active, unexpired tokens. Rotate by creating a replacement, updating CI, and revoking the old token. There are no CLI login, token-create, token-list, or token-revoke commands.

Set the token and the widget API URL from the project's Embed tab:

export CHATTYBOX_DEPLOY_TOKEN='cb_cfg_v1_...'
export CHATTYBOX_API_URL='https://your-deployment.convex.site/chat'

Never commit the token. Store it in your CI provider's encrypted secret store.

Deployment tokens authenticate as Authorization: Bearer ...; public widget keys cannot replace them. Management routes do not use widget origin restrictions, do not require an Origin header, and do not provide browser CORS access. This is not an origin-bound secret: anyone holding it can call the API from a server. Missing, malformed, expired, revoked, wrong-environment, or otherwise invalid tokens all return HTTP 401 INVALID_TOKEN. Tokens also stop working when their project or owner is missing or being deleted.

Use the HTTP actions deployment root or its widget URL ending in /chat, not a .convex.cloud client URL or a full /v1/config/deployments endpoint. The CLI removes trailing slashes and a final /chat, then appends the management route. HTTPS is required except for localhost, 127.0.0.1, or [::1]; credentials, query strings, and fragments in the API URL are rejected. Verify the destination before supplying a secret.

Config Lock

After verifying the deployment workflow, optionally enable Config-as-code lock in the same settings section. This is irreversible through the current dashboard, CLI, and public API: there is no supported unlock operation. Enabling it does not create a token or deploy a version. Keep a tested token workflow before locking.

The lock blocks dashboard writes to config-owned project/widget settings, icon uploads, and manual corpus operations such as scraping, deleting pages, clearing content, and regenerating embeddings. It does not stop scheduled work or project deletion. Token creation/revocation, public-key management, and operational reads remain available.

Branding is the exception: the dashboard's branding-only control remains editable on locked projects. Hiding attribution requires Starter or higher, including through config deployment. Omitting widget.showBranding preserves the current stored preference (or defaults to true if none exists); an explicit value overwrites it on the next runtime promotion. Free-plan runtime output still enforces branding.

Deploy Configuration

Deploying creates an immutable version and promotes it to the selected environment:

bunx @openstaticfish/chattybox-cli deploy --environment preview
bunx @openstaticfish/chattybox-cli deploy --environment production

Environment labels do not isolate runtimes. development only records a version/pointer (applied: false, indexingStatus: "not_required"). Both preview and production overwrite the same project's runtime settings and use the same corpus and public widget keys. The most recent runtime promotion wins, even if another label's status still points to an older version. A preview-scoped token can therefore change a project serving production traffic. Use separate projects and their respective tokens/keys for actual isolation.

Preview and production atomically apply configuration and queue a scrape job (applied: true, indexingStatus: "pending"). This does not wait for scraping or embeddings to finish. Refreshes update pages in place, request embeddings when content changes, and do not prune old URLs merely because a new source/filter omits them. The corpus is not a versioned snapshot: partial updates can remain if work fails, and failed refreshes do not revert the applied config. Jobs record a config version ID but also read mutable scrape settings; avoid overlapping deployments when indexing consistency matters.

Replacement and Plan Limits

Deploy complete desired configuration, not a patch. Runtime promotion clears or resets omitted config-owned values, including previous dashboard settings:

Omitted valueRuntime effect
project.description, assistant prompt/profile/fallbackClears stored overrides; runtime fallback behavior applies.
runtimeResets to automatic locale, default en, locale override allowed.
widget or optional widget copy/theme/positionEnables the widget; clears omitted presentation overrides so runtime defaults apply.
widget.iconResets to default icon, medium size; unlinks any uploaded icon.
Source mode/filters/scheduleMode defaults to crawl; omitted filters clear; automatic refresh defaults to off. Mode-specific settings are only applied for their matching mode.
widget.showBrandingException: preserves the existing stored preference, otherwise true.

maxPages stores the requested value without clamping it to the base plan. Each run limits outbound discovery and selection to the smaller of that request and available capacity. Existing indexed pages and available capacity are pooled at the owner/account level; this is not a per-project capacity contract or a promise that all requested pages will be indexed. At zero or denied capacity, only known existing pages may refresh; a project with none makes no unknown-page outbound discovery. Omission uses the plan's included account-page allowance as the request default: Free 10, Starter 150, Pro 5,000, and Business 25,000. This does not add owner-pool or plan entitlements. A scrape separately rechecks current page/PAYG allowance and monthly refresh limits.

Automatic daily refresh and explicit showBranding: false require Starter or higher. Free accepts weekly or monthly automatic refresh. These entitlement checks happen on preview/production promotion, including rollback, not during local validation or development recording. A previously valid version can fail promotion after a plan change.

Promotion resolves the plan from server-cached project/workspace data, not a live billing-provider request. Verify billing synchronization and actual scrape allowance separately when diagnosing a recent plan/PAYG change.

Use --json in automation to receive the API result, including version._id, the environment pointer, applied, indexingStatus, and the scrape job ID when one is queued. Keep version._id for later promotion or rollback; the human-readable sequential version number is not a usable --version argument.

Status, Promotion, and Rollback

bunx @openstaticfish/chattybox-cli status --environment production

bunx @openstaticfish/chattybox-cli promote \
--version '<config-version-id>' \
--environment production

bunx @openstaticfish/chattybox-cli rollback \
--version '<known-good-config-version-id>' \
--environment production

--version takes the opaque config version ID from deploy/promote/rollback JSON (version._id) or status JSON (activeVersion._id), not the displayed number, a Git SHA, or a config hash. Quote placeholder IDs as shown, then replace them with real IDs belonging to the token's project.

Rollback uses the same backend operation as promote. It need not target an older version and has no automatic previous-version selection. It preserves immutable config version records but overwrites the selected environment pointer and its promotion metadata; there is no append-only promotion event history. Preview/production rollback reapplies config under current entitlements and queues another refresh, not an exact historical content restore. Repeated deploys create new versions even for identical config; repeated promotions can queue new jobs.

status prints the selected label's Environment version and pointer summary, not the effective runtime config, a dashboard-drift diff, or scrape/embedding progress. An undeployed label prints Environment version: none and returns environment: null and activeVersion: null. The separate runtimeConfigVersionId identifies the last configuration applied to the shared runtime and can differ after another label is promoted. Successful status calls also update the token's lastUsedAt and consume management rate limit capacity. Check indexing separately in the dashboard.

Commands, Flags, and Errors

CommandArguments and effect
init [path]Defaults to chattybox.config.json; creates parent directories; refuses to overwrite an existing file.
validate [path]Same default path; reads and validates JSON without remote access.
deployRequires --environment; optional --config <path>; validates then creates and promotes a version.
promote, rollbackRequire --environment and --version <opaque-id>; do not read a local config.
statusRequires --environment; does not read a local config.
No command, help, --help, -hTop-level help. Use chattybox --help, not chattybox deploy --help.
--version, -vTop-level CLI version; unrelated to promotion's --version flag.

All four management commands accept --token, --api-url, and --json. Explicit credentials/URL flags take precedence over CHATTYBOX_DEPLOY_TOKEN and CHATTYBOX_API_URL. Prefer secret environment variables over tokens in shell history or process arguments. Value flags accept --flag value or --flag=value; --json is a bare switch. There is no default environment. Unknown, duplicate, empty, and command-inapplicable management options are rejected. Local commands accept only one positional path and reject flags/extra arguments.

There are no --dry-run, --wait, --force, --project, message, or optimistic-concurrency flags. The HTTP API supports expected-version fields, but the CLI does not send them. Serialize CI deployments to the same project.

Successful executable commands exit 0. File/JSON/validation, argument, transport, response-read, and HTTP failures exit 1 with a ChattyBox: ... message on stderr. --json formats successful results only; it does not produce JSON errors. API errors include their HTTP status and error code when supplied; successful non-JSON responses are rejected. The CLI has no automatic retry, polling, or configured request timeout. After a transport failure, check status before retrying because the server may already have committed the deployment.

Management routes are POST /v1/config/deployments (201), POST /v1/config/deployments/promote and /rollback (200), and GET /v1/config/deployments/status?environment=... (200). Success is wrapped in { "data": ... } over HTTP; CLI JSON unwraps it. Errors normally use { "error": { "code": ..., "message": ... } }: 401 INVALID_TOKEN, 404 NOT_FOUND for a missing/other-project version, 409 CONFIG_CONFLICT, 413 BODY_TOO_LARGE, 422 INVALID_CONFIG, or 500 INTERNAL_ERROR. Malformed typed IDs are 422, not necessarily 404. The request-body limit is 262,144 bytes. All management routes share a per-token limit of 20 requests per minute; 429 responses use the shared rate-limit format and Retry-After header. A completely absent POST body currently falls through to 500, unlike malformed JSON (422).

Status activeVersion is the selected environment label's last promoted version. The separate runtimeConfigVersionId is the last configuration applied to the shared project runtime and may differ after another label is promoted. A development promotion does not change that runtime pointer. If configuration is not locked, later dashboard edits can also change runtime fields without changing the last-applied configuration ID.

CI/CD Workflows

Add CHATTYBOX_DEPLOY_TOKEN as an encrypted, production-scoped secret in your CI provider. Add CHATTYBOX_API_URL as a secret or variable using the widget API URL from the project's Embed tab. Run validation on pull requests, but restrict production deployment to your protected default branch or an approved deployment environment.

GitHub Actions

name: Deploy ChattyBox configuration

on:
pull_request:
push:
branches: [main]

jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- uses: actions/setup-node@v6
with:
node-version: 24
- run: bunx @openstaticfish/chattybox-cli validate

deploy:
if: github.event_name == 'push'
needs: validate
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- uses: actions/setup-node@v6
with:
node-version: 24
- name: Validate and deploy
env:
CHATTYBOX_DEPLOY_TOKEN: ${{ secrets.CHATTYBOX_DEPLOY_TOKEN }}
CHATTYBOX_API_URL: ${{ vars.CHATTYBOX_API_URL }}
run: |
bunx @openstaticfish/chattybox-cli validate
bunx @openstaticfish/chattybox-cli deploy --environment production --json

Use an environment-scoped production secret and require deployment approval when your repository supports it. The CLI records common CI commit and branch variables with the immutable version.

GitLab CI/CD

Store both values under Settings → CI/CD → Variables. Protect the deployment token, mask it, and scope it to the production environment.

.gitlab-ci.yml
image: oven/bun:1

validate_chattybox:
stage: test
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
script:
- bunx --bun @openstaticfish/chattybox-cli validate

deploy_chattybox:
stage: deploy
environment: production
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
script:
- bunx --bun @openstaticfish/chattybox-cli validate
- bunx --bun @openstaticfish/chattybox-cli deploy --environment production --json

GitLab exposes CHATTYBOX_DEPLOY_TOKEN and CHATTYBOX_API_URL automatically to the job using their variable names. Use a protected default branch so protected variables are unavailable to untrusted branches. The Bun container examples explicitly use --bun so they do not depend on a separate Node installation; verify your pinned Bun/image version in CI.

Bitbucket Pipelines

Add both values as deployment variables under Repository settings → Pipelines → Deployments → Production. Mark the deployment token as secured.

bitbucket-pipelines.yml
image: oven/bun:1

pipelines:
pull-requests:
'**':
- step:
name: Validate ChattyBox configuration
script:
- bunx --bun @openstaticfish/chattybox-cli validate
branches:
main:
- step:
name: Deploy ChattyBox configuration
deployment: Production
script:
- bunx --bun @openstaticfish/chattybox-cli validate
- bunx --bun @openstaticfish/chattybox-cli deploy --environment production --json

Replace main if your default branch has another name. Bitbucket injects deployment variables only into steps associated with that deployment environment. The CLI has no dedicated BITBUCKET_* metadata mapping; it falls back to local Git for commit and branch, which may have no branch name in a detached checkout.

Configuration Sections

SectionPurpose
schemaVersionSelects the public configuration contract. Version 1 is currently supported.
projectOptional project metadata such as its dashboard description.
assistantNames the assistant and defines a built-in persona or custom system prompt plus fallback copy.
knowledge.sourcesSelects homepage, sitemap, manual URL, or crawl discovery plus limits, filters, and refresh schedule.
runtimeControls automatic/fixed locale, default locale, and page-level locale overrides.
widgetControls enablement, position, copy, colors, and default/emoji/URL icon presentation.

Personas

Set promptProfile to default, support, sales, or sarcastic to use the maintained persona instructions. Set it to custom and provide systemPrompt for a custom persona. If both a built-in profile and systemPrompt are provided, the explicit prompt wins.

Knowledge discovery

This expanded configuration fragment requires the published 0.3.1 config types and the matching 0.3.0 backend contract. Its daily schedule requires Starter or higher; maxPages: 500 stores a request that remains subject to the run's effective capacity.

{
"type": "website",
"url": "https://docs.example.com",
"mode": "sitemap",
"sitemapUrl": "https://docs.example.com/docs-sitemap.xml",
"include": ["/docs/"],
"exclude": ["/private/"],
"maxPages": 500,
"autoRescrape": true,
"rescrapeInterval": "daily"
}

Supported modes are:

  • homepage: select only url for this refresh (existing pages are not deleted).
  • sitemap: use sitemapUrl; an explicit sitemap yielding no URLs fails. If omitted, try /sitemap.xml, then /sitemap_index.xml, then the homepage when neither yields URLs.
  • manual: index the non-empty manualUrls list.
  • crawl: try conventional sitemaps, then follow links from url up to maxDepth (default 3).

include and exclude use case-sensitive URL substring matching, not glob or regex syntax. Any include match admits a URL; any exclude match rejects it, even if included. Empty lists do not filter. Filters also apply to homepage and manual selection.

maxPages must be a positive integer; its stored request is not clamped, while per-run outbound discovery and selection use the effective capacity described above. maxDepth must be an integer from 0 through 20 and controls link-crawl fallback, not sitemap enumeration. Omitted mode selects crawl and preserves an explicit maxDepth, including zero. autoRescrape: true requires rescrapeInterval: "daily", "weekly", or "monthly"; an interval alone does not enable scheduling. Monthly means 30 days, and the scheduler may run after the due time.

Locale and Widget Validation

Supported locale codes are en, de, fr, es, it, nl, pl, pt, sv, fi, et, cs, cy, and id. localeMode is auto or fixed; fixed requires defaultLocale. allowLocaleOverride is independently configurable. The shorthand runtime.locale selects fixed mode and disables overrides; current local validation rejects combining it with localeMode, defaultLocale, or allowLocaleOverride: true.

Widget positions are bottom-right, bottom-left, top-right, and top-left. All three theme colors use #RRGGBB, not short hex, named colors, or alpha. Icons use default, emoji (non-empty emoji string), or url (HTTP(S) url), with optional small, medium, or large size. Validation does not fetch icon URLs, check image MIME types, or require an emoji string to contain exactly one emoji. Source and icon URL validation checks syntax/protocol, not reachability or permission to crawl. Optional description, assistant prompt/fallback, widget title, and welcome copy must be non-empty when supplied; omit them to clear overrides.

Typed Configuration Helper

Install the configuration package when building separate TypeScript tooling around the schema. The published 0.3.1 types support this baseline example:

bun add -d @openstaticfish/chattybox-config
import { defineConfig } from '@openstaticfish/chattybox-config';

const config = defineConfig({
schemaVersion: '1',
assistant: { name: 'Support' },
knowledge: {
sources: [{ type: 'website', url: 'https://docs.example.com' }],
},
widget: { enabled: true },
});

The CLI reads JSON files. defineConfig() is for typed application or build tooling; it does not make TypeScript files directly loadable by the CLI. Both defineConfig(config) and validateConfig(config) synchronously validate, throw an ordinary Error on the first failure, and return the same object without cloning, freezing, coercing, or inserting defaults. The package exports TypeScript types and these functions, not a JSON Schema document or $schema URL. The published 0.3.1 package provides TypeScript support for the expanded fields above.

For a working production integration today, use the hosted widget or build a custom interface with the JavaScript SDK.

We use optional analytics and tag-management tools to understand site use. Choose whether to allow Ahrefs Web Analytics, PostHog, and Google Tag Manager. Turning analytics off reloads this page so the change takes effect cleanly. Essential site functionality and error monitoring are not controlled by this choice. Read our privacy policy.