Operate

Configuration

Configuration

clickclack serve resolves config in this order. Later sources override earlier ones for any given key:

  1. Hard-coded defaults (Addr=":8080", Data="./data").
  2. JSON config file passed via --config.
  3. Environment variables.
  4. CLI flags that were explicitly set.

An explicitly present dev_bootstrap value in the config file is the one exception: it is not replaced by CLICKCLACK_DEV_BOOTSTRAP. This prevents a stale process environment from silently enabling development authentication against a deployment file that explicitly disables it.

Source: apps/api/internal/config/config.go and the applyFlagOverrides hook in cmd/clickclack/main.go.

#Flags and env vars

FlagEnvDefaultNotes
--addrCLICKCLACK_ADDR:8080HTTP listen address.
--dataCLICKCLACK_DATA./dataData root for DB, uploads, logs.
--dbCLICKCLACK_DBderivedDB URL. Defaults to sqlite://<data>/clickclack.db.
--uploadsCLICKCLACK_UPLOADSderivedUpload storage URL. Defaults to file://<data>/uploads; use r2://bucket/prefix for Cloudflare R2.
--environmentCLICKCLACK_ENVIRONMENTunsetLow-cardinality deployment label used only by opt-in metrics.
--metrics-enabledCLICKCLACK_METRICS_ENABLEDfalseExpose metadata-only Prometheus metrics at /metrics; keep private.
--configunsetJSON config file.
--dev-bootstrapCLICKCLACK_DEV_BOOTSTRAPfalseserve only. Creates a default user/workspace/channel and enables local dev auth fallbacks when explicitly set to true.
CLICKCLACK_PUBLIC_URLunsetCanonical external origin. Required for GitHub OAuth and namespaced cookies.
CLICKCLACK_PUBLIC_API_URLpublic URLCanonical external API base. May use a different origin and a normalized base path.
--embed-frame-ancestorsCLICKCLACK_EMBED_FRAME_ANCESTORSunsetComma- or whitespace-separated exact origins allowed to frame /embed/*; see Embedded threads.
--access-team-domainCLICKCLACK_ACCESS_TEAM_DOMAINunsetCloudflare Access team HTTPS origin. Must be configured together with the Access audience.
--access-audCLICKCLACK_ACCESS_AUDunsetExpected Cloudflare Access application audience tag. Must be non-empty when the team domain is set.
CLICKCLACK_COOKIE_NAMESPACEunsetStable lowercase cookie namespace for multiple trusted ClickClack instances on one hostname.
CLICKCLACK_GITHUB_CLIENT_IDunsetGitHub OAuth app client ID.
CLICKCLACK_GITHUB_CLIENT_SECRETunsetGitHub OAuth app client secret.
CLICKCLACK_GITHUB_ALLOWED_ORGunsetOptional GitHub org login gate. Requires read:org scope.
CLICKCLACK_GITHUB_MODERATOR_ORGunsetOptional GitHub org whose members become guest-workspace moderators. Requires read:org scope.
CLICKCLACK_PUSHOVER_API_TOKENunsetPushover application API token. Users still opt in with their own Pushover user key in account settings.
CLICKCLACK_R2_ACCOUNT_IDunsetCloudflare account ID for r2:// uploads.
CLICKCLACK_R2_ACCESS_KEY_IDunsetR2 API token access key ID.
CLICKCLACK_R2_SECRET_ACCESS_KEYunsetR2 API token secret access key.
CLICKCLACK_R2_ENDPOINTderivedOptional S3-compatible endpoint override for tests or non-standard R2 endpoints.

#Config file

{
  "addr": ":8080",
  "data": "./data",
  "db": "sqlite:
   "file:///var/lib/clickclack/uploads",
  "environment": "staging",
  "metrics_enabled": false,
  "dev_bootstrap": false,
  "public_url": "https:
   "https://api.example.com/services/clickclack",
  "embed_frame_ancestors": ["https:
   "https://openclaw.cloudflareaccess.com",
  "access_aud": "<application-audience-tag>",
  "cookie_namespace": "production",
  "github_client_id": "Iv1.xxxxxxxxxxxx",
  "github_client_secret": "...",
  "github_allowed_org": "openclaw",
  "github_moderator_org": "openclaw",
  "pushover_api_token": "azGDORePK8gMaC0QOYAMyEEuzJnyUi",
  "r2_account_id": "91b59577e757131d68d55a471fe32aca",
  "r2_access_key_id": "...",
  "r2_secret_access_key": "..."
}

Pass with --config /etc/clickclack/config.json. Values from the file are overridden by environment variables; CLI flags override both when explicitly set. The dev_bootstrap exception is described above.

The Access team domain is an HTTPS origin without credentials, a path, query, or fragment. access_team_domain and access_aud are an all-or-nothing pair; when both are absent, trusted-proxy authentication is disabled. See Trusted proxy (Cloudflare Access) for verification, provisioning, and session behavior.

#Public frontend and API URLs

CLICKCLACK_PUBLIC_URL is an origin, not an application base path. It must:

  • use HTTPS for every non-loopback host
  • contain a host and optional non-default port
  • contain no credentials, path, query, fragment, or trailing-dot hostname

For example, https://chat.example.com and http://127.0.0.1:8080 are valid. http://chat.example.com, https://chat.example.com/clickclack, and https://user@chat.example.com fail startup validation. GitHub OAuth credentials also fail startup validation unless the public URL is set.

CLICKCLACK_PUBLIC_API_URL is the canonical address browsers and installers use for API calls. It defaults to CLICKCLACK_PUBLIC_URL, so existing same-origin deployments require no change. Set it only when the API has a different public origin or a public base path:

CLICKCLACK_PUBLIC_URL=https://chat.example.com
CLICKCLACK_PUBLIC_API_URL=https://api.example.com/services/clickclack

The API URL follows the same scheme, host, credential, query, and fragment rules. It may additionally contain a normalized base path made from ordinary URL path segments; trailing slashes are removed. Dot segments, doubled slashes, encoded separators, whitespace, and backslashes fail startup validation. A path-mounted ingress must route /services/clickclack/api/* to the Go server's /api/* routes by stripping the configured prefix.

Loopback split origins must both use HTTP and exactly the same hostname; only their ports may differ. This keeps local session cookies same-site. Remote origins must use HTTPS.

When both URLs are configured, ClickClack injects the canonical API base into the SPA HTML it serves. A separate frontend ingress should proxy the SPA and asset routes to the ClickClack server while browsers call the configured API origin directly. A separately hosted static build must inject the equivalent value before the app modules run:

<script>window.__CLICKCLACK_CONFIG__ = { apiBaseUrl: "https://api.example.com/services/clickclack" };</script>

That value is browser routing only. The server remains authoritative for setup claim URLs and returns only URLs derived from validated administrator configuration.

The default cookie names remain cc_session and cc_oauth_binding. Set CLICKCLACK_COOKIE_NAMESPACE only when multiple trusted ClickClack instances must share one hostname:

CLICKCLACK_PUBLIC_URL=https://chat.example.com:8443
CLICKCLACK_COOKIE_NAMESPACE=production

The namespace must be at most 32 characters and contain lowercase letters, digits, and interior hyphens. HTTPS deployments receive __Host- cookie names, such as __Host-cc-production-session; path-mounted APIs use the path-compatible __Secure- prefix and scope cookies to the configured API base path. Loopback HTTP uses cc-production-session.

Treat the namespace as durable deployment identity:

  • Every replica serving the same public origin and database must use the same
  • namespace.

  • Different instances on the same hostname must use different namespaces.
  • Changing it signs browsers out and strands in-progress OAuth browser state
  • until that state expires.

Cookies are scoped to hostnames, not ports. Namespaces prevent accidental same-name collisions; they are not a security boundary between mutually untrusted services. Put untrusted instances on separate hostnames. Use separate registrable domains when compromise of one deployment must not affect another.

#DB URL

SQLite forms:

sqlite://./data/clickclack.db
./data/clickclack.db

Both end up at the same place — the sqlite:// prefix is stripped. The parent directory is created on open.

Postgres forms:

postgres://user:pass@host:5432/clickclack?sslmode=require
postgresql://user:pass@host:5432/clickclack?sslmode=require

serve, migrate, and admin commands all accept --db or CLICKCLACK_DB. Postgres stores durable chat state in the external database.

#Upload storage

Local disk is the default:

CLICKCLACK_UPLOADS=file:///var/lib/clickclack/uploads

Cloudflare R2 uses the S3-compatible API:

CLICKCLACK_UPLOADS=r2://clickclack-uploads/prod
CLICKCLACK_R2_ACCOUNT_ID=91b59577e757131d68d55a471fe32aca
CLICKCLACK_R2_ACCESS_KEY_ID=...
CLICKCLACK_R2_SECRET_ACCESS_KEY=...

The database still stores upload metadata and auth visibility. The upload backend stores the bytes and streams them back through /api/uploads/{id} after the normal ClickClack permission checks.

#Disabling dev fallbacks

For non-local deployments:

clickclack serve \
  --dev-bootstrap=false \
  --config /etc/clickclack/config.json

Combine with real auth (CLI-created magic links or GitHub OAuth) so the "first-user-in-DB" dev auth fallback never kicks in. In containers, this is already the default; CLICKCLACK_DEV_BOOTSTRAP=false is only an explicit guard.