Skip to content
CADCAMFUN
Docs

Documentation

Sidecar

The local Bun service that owns persistence, authenticated HTTP, manufacturing services, project history, and the assistant.

Runtime responsibilities

The sidecar is the Bun backend shared by the desktop and local development integrations. It opens SQLite, supplies repositories and domain services, serves the schema-defined HTTP API, and hosts assistant and workflow integration. The Tauri shell starts the packaged binary and discovers its assigned port from a machine-readable startup event.

apps/sidecar/src/layers.ts composes the served API, handlers, authorization, repositories, and services. Optional providers such as paid checkout and AI models are configured through their services; their absence does not turn a database record into a working external integration.

Process and shutdown protocol

The main HTTP server binds only 127.0.0.1, with port 0 so the operating system chooses an available port. HTTP idle timeout is disabled to allow a long assistant call to remain silent between streamed events. Graceful HTTP shutdown is configured for 500 milliseconds before scope finalization continues.

After the API's public health probe succeeds, the process prints a ready event on stdout. Startup failure prints an error event and sets a failing exit code. Operational logs go to stderr as JSON; stdout is reserved for the parent process protocol.

Startup protocol examples
{"event":"ready","port":53211}
{"event":"error","message":"Startup failure details"}

A newline-terminated stdin message with event: "shutdown", stdin closure, SIGINT, or SIGTERM releases the server scope. Malformed parent messages are ignored, and an oversized incomplete line is discarded. Finalizers stop owned services and checkpoint/close SQLite. Interrupt-only termination is reported as a clean exit.

Required configuration

AppConfigService reads process configuration once in apps/sidecar/src/config.ts. CADCAMFUN_TOKEN is the required application bearer token and CADCAMFUN_DATA_DIR is the required application data directory. The database path is <dataDir>/cadcamfun.db. CADCAMFUN_MIGRATIONS_DIR is optional and supplies the SQL migration folder; without it the database layer does not apply application migrations.

The desktop supplies launch configuration. For direct development, provide those configuration names in the launching shell and run the package's start script from the repository root:

Start a configured sidecar
bun run --filter @cadcamfun/sidecar start

The sidecar package defines start and typecheck scripts. Packaged binary compilation is owned by the repository release scripts, rather than a sidecar-package build command.

Optional configuration

  • CADCAMFUN_APP_URL sets the base for authentication links; CADCAMFUN_DEBUG_LINKS enables returning debug reset/magic links and defaults to false.
  • CADCAMFUN_AI_HOST is nikcli (default; chat runs through the embedded conversation host) or direct (AI SDK only). CADCAMFUN_AI_OFFLINE (boolean, default false) disables the optional model-catalogue network path. CADCAMFUN_AI_MINIMAX_BASE_URL overrides MiniMax's endpoint.
  • STRIPE_SECRET_KEY enables the Stripe provider; STRIPE_PRICE_BASIC, STRIPE_PRICE_PRO, and STRIPE_PRICE_ENTERPRISE supply paid-plan prices. STRIPE_WEBHOOK_SECRET is required for signed webhook verification.
  • CRON_SECRET supplies the cron route secret; without it the local application can call that route without an additional cron secret. ADMIN_EMAIL is a legacy administrator identity check; by default the local profile is the administrator of its own installation.
  • OTEL_EXPORTER_OTLP_ENDPOINT enables the configured OTLP telemetry export. Without an endpoint, external telemetry export is off.

All of these names are read in one place, AppConfigService; the token and Stripe secret key are held as redacted values so they do not appear in logs. AI provider keys are managed through the credential API and dedicated credential file. They are not read as vendor API-key environment settings by AppConfigService.

HTTP access and API discovery

The sidecar publishes /api/openapi.json for its served contract and uses bearer middleware on protected groups. A second optional session header chooses the acting account. The local profile is created or reused under local@cadcamfun.local, keeping the installation usable without account sign-in.

CORS admits tauri://localhost, HTTP/HTTPS tauri.localhost, and HTTP localhost or 127.0.0.1 origins with an optional port. Other origins receive no allowed-origin response header. CORS is a browser policy; the bearer middleware remains the access control for protected routes.

The served groups cover design resources, manufacturing generation and verification, exact CAD, libraries, costs, AI, VCS/workflows, events, companion controls, identity, organizations, notifications, files, plugins, billing, analytics, waitlist, MCP, and local state. Consult the running OpenAPI document for method and payload details.

The acting-account header is x-session-token. Without it, or with a token that does not verify as a session token for an existing user, requests act as the local profile (src/http/authorization.ts).

Source organization

main.ts launches the scoped server and parent protocol. config.ts supplies configuration, layers.ts wires dependencies, startup.ts shares readiness and the loopback port, shutdown.ts reads parent shutdown messages, and observability.ts configures optional telemetry.

http/ maps validated requests to services. repos/ maps resources to Drizzle queries and resource access rules. services/ coordinates cross-resource behavior, and plugins/ contains worker-host and browser bootstrap implementations.

Handler groups

Handlers are grouped by resource: projects, drawings, components, toolpaths and extras, the toolpath generator, exact CAD, materials/tools/machine configurations and their storage routes, library, costs, files, organizations, notifications, user/auth, billing, analytics, waitlist, MCP, miscellaneous application services, key-value state, AI, VCS, events, and companion management.

entity-io.ts shares entity import/export helpers. authorization.ts applies application and scoped bridge credentials. A handler's declared payload has already passed schema decoding; service-level validation still checks references, ownership, evidence consistency, and domain constraints.

Domain service composition

AI services supply drawing and CAM actions, parametric/exact inspection, project context, memory, worker journals, and plugin tools. cam-program.ts reruns captured manufacturing requests and validates native evidence during saves. History services apply snapshots, protected-branch rules, GitHub transport, and workflow execution.

Other services own file storage, cost calculations, local profile and acting-user state, token signing/revocation, bridge credentials, payments, notifications, live events, and companion lifecycle. Effect layers own their resources so shutdown releases them in dependency order.

Repository boundary

Repositories use the shared Database.query service for persistence and typed storage failures. They decode stored JSON and enforce resource-specific access or provide authorization helpers. HTTP handlers translate domain/storage failures into their declared response errors; geometry calculations stay in @cadcamfun/core.

Plugin integration

The general worker host is in plugins/host.ts. The production assistant plugin service loads explicitly enabled ai:tool extensions from the application plugin directory. The browser ZIP installer and bootstrap API use their own manifest and storage paths. See Plugins before selecting an extension format or assuming a capability is wired.

Companion HTTPS gateway

The companion gateway is a separate opt-in HTTPS listener that can resume a previously enabled setting. It binds IPv4 for network clients, presents a generated certificate retained under <dataDir>/companion, and uses scoped revocable device tokens. Pairing supplies a certificate fingerprint so the mobile transport can pin the desktop.

The gateway checks an explicit route allow-list and forwards admitted requests to the loopback API with the application token. A paired phone never receives that application token. The allow-list excludes credential administration, plugin installation, file management, and billing. Revoking a device or disabling the gateway removes its network access.

Readiness and diagnostics

The ready event means the local HTTP health probe succeeded after server layer construction. It does not mean a model provider has accepted a request, checkout is configured, or every optional network service is reachable. Diagnose those services through their own status and result endpoints.

For a startup error, retain the protocol message and relevant stderr events, check configuration and migrations, and inspect the data directory's accessibility. Never paste the launch bearer token or provider credential contents into diagnostic output.

Data ownership

Persistent data includes the SQLite database and migration backups, file storage, AI credential/configuration state, authentication secret and revocation records, plugin assets, and companion certificates. Protect the whole application data directory when backing it up or transferring an installation. The sidecar owns connection lifetime; clients own neither the SQLite handle nor the main bearer token's issuance.

Launch a disposable local instance

Run these commands at the repository root in a dedicated terminal. The directory is new, the token is generated locally without printing it, migrations are explicit, and direct/offline AI mode avoids importing the embedded conversation host for this startup check. openssl supplies the random launch token in this shell example.

Explicit development configuration
export CADCAMFUN_DATA_DIR="$(mktemp -d /tmp/cadcamfun-docs.XXXXXX)"
export CADCAMFUN_TOKEN="$(openssl rand -hex 32)"
export CADCAMFUN_MIGRATIONS_DIR="$PWD/packages/db/drizzle"
export CADCAMFUN_AI_HOST=direct
export CADCAMFUN_AI_OFFLINE=1
bun run --filter @cadcamfun/sidecar start

Read the ready event's port rather than assuming a fixed number. Keep stdin open while the service runs; piping from an already-ended input source asks it to shut down. Use SIGTERM or the shutdown protocol to release the database. The disposable directory remains available for inspection after exit.

With a connection URL and its token already available in a client shell, compare public liveness with protected access:

Probe a running instance
curl --fail-with-body "$CADCAMFUN_URL/api/health"
curl --fail-with-body -H "Authorization: Bearer $CADCAMFUN_TOKEN" "$CADCAMFUN_URL/version"
curl --fail-with-body -H "Authorization: Bearer $CADCAMFUN_TOKEN" "$CADCAMFUN_URL/api/projects"

Source: apps/sidecar/src/main.ts, config.ts, shutdown.ts, and test/process.test.ts.

Logs and optional telemetry

Stderr JSON logs and stdout ready/error events have different consumers. A launcher should parse complete stdout lines and forward or retain stderr separately. A request that fails after readiness is an application error, not a second startup event.

When OTEL_EXPORTER_OTLP_ENDPOINT is configured, ObservabilityLive sets up batching for spans/logs and periodic metric export. It appends /v1/traces, /v1/metrics, and /v1/logs to the supplied collector base after removing trailing slashes. Supply a collector base such as http://collector:4318, rather than an already suffixed signal URL.

The resource service name is cadcamfun-sidecar and its version comes from application configuration. Standard OpenTelemetry resource environment settings are handled by the SDK. Network export is optional, while local stderr remains available when no collector is configured.

Source: apps/sidecar/src/observability.ts and main.ts.

Identify the failing boundary

SymptomLikely boundary and check
Process exits before readyRead the protocol error and stderr. Check required config, directory access, migration identity, and service construction.
Health answers but protected routes return 401Use the token for this launch, not an account session token or a token from a prior launch. A bridge token opens only its two action routes.
A request works in curl but fails in a browserInspect browser origin and preflight response. The allow-list permits the supported Tauri and HTTP loopback origins; bearer access is still required.
Ready port is unreachable after launchCheck whether the parent closed stdin or the shell restarted the child. The main API binds loopback only and is not the phone-facing gateway.
Companion gateway fails to enableRead certificate, port-bind, and persisted-settings errors. Explicit ports must be integers from 1024 to 65535; failure to save settings stops the new listener.
Data appears in one tool but not anotherCompare explicit database paths for the sidecar, web Studio, and migration CLI before assuming resources were lost.

The real-process lifecycle tests cover EOF, a split shutdown JSON line, and SIGTERM, then restart the same data directory and verify persisted projects. They also inspect the WAL after exit. This establishes the source process's lifecycle behavior; packaged shell and machine-specific delivery still require their own validation.

Source: apps/sidecar/test/process.test.ts, src/http/authorization.ts, src/layers.ts, and src/services/companion.ts.

Startup sequence in detail

Tauri shellsidecar (Bun)webview spawn: CADCAMFUN_TOKEN (64 hex), DATA_DIR, MIGRATIONS_DIR stdout {"event":"ready","port":N} (≤ 60 s) invoke get_sidecar_endpoint (waits ≤ 125 s) { url, token } GET /api/health, then API calls quit: stdin {"event":"shutdown"}
  1. The shell generates a launch token of 32 random bytes, hex-encoded as 64 characters, and starts the sidecar with CADCAMFUN_TOKEN, CADCAMFUN_DATA_DIR, and CADCAMFUN_MIGRATIONS_DIR.
  2. AppConfigService reads all settings once. A missing required setting fails startup with an error event.
  3. The database layer opens <dataDir>/cadcamfun.db, enables WAL mode and foreign keys, and applies migrations from the migrations directory.
  4. Layers are composed: database, then repositories, then services, then HTTP handlers. Every resource is owned by a layer, so shutdown releases it in reverse dependency order.
  5. The HTTP server binds 127.0.0.1 on port 0, and the operating system picks a free port.
  6. The public health route is probed. Only after it answers is {"event":"ready","port":N} written to stdout.
  7. The shell waits up to 60 seconds for that line and restarts the sidecar at most once. The webview waits up to 125 seconds for get_sidecar_endpoint, then connects.
StreamCarriesConsumer
stdoutExactly one ready or error JSON lineThe parent process (shell or your script).
stderrJSON operational logsLog files and diagnostics. Lines that may carry credentials (for example containing Bearer, secret, or cadcamfun_token) are redacted by the shell.
stdin{"event":"shutdown"} lines; EOF also shuts downThe sidecar's shutdown reader. Oversized or malformed lines are ignored.

Authentication model

CredentialWhere it comes fromWhat it opens
Launch token (Authorization: Bearer)Generated by the shell per launch, or set by you for manual runsEvery protected route of the loopback API.
Session token (x-session-token)Sign-in flows; signed by a per-installation secret stored next to the databaseSelects the acting user. If missing or invalid, requests act as local@cadcamfun.local.
Bridge tokenPOST /api/ai/bridge/tokens; shown onceOnly the action listing and invocation routes, for one project, read or read-write, until expiry (default 30 days, maximum 365).
Companion device tokenPairing; shown once inside the pairing link; stored only as a SHA-256 hashThe companion gateway's allow-listed routes, over HTTPS.

Signed tokens use HS256 with issuer cadcamfun. Revoked tokens verify as invalid, and unknown or forged tokens are ignored by revocation. The signing secret is generated once per installation and stored in the data directory, so links issued before a restart still verify afterwards.

Keep the launch token out of logs

Treat the launch token like a password for the whole local API. Scripts should read it from the environment and never print it. Use a bridge or device token when another program or device needs narrower access.

What lives in the data directory

PathContent
cadcamfun.db (+ -wal, -shm)SQLite database in WAL mode.
auth-secretPer-installation signing secret for session and link tokens.
companion/Gateway certificate and private key (owner-only permissions).
plugins/enabled.json and one folder per assistant plugin.
file storageUploaded files (local bucket).

Back up the whole directory while the sidecar is stopped, or after a shutdown that checkpointed the WAL. Copying only cadcamfun.db while the sidecar runs can miss recent writes that are still in the WAL file.

The ready line, captured

Start a disposable sidecar and read its port
CADCAMFUN_TOKEN=t CADCAMFUN_DATA_DIR="$(mktemp -d)" \
CADCAMFUN_MIGRATIONS_DIR="$PWD/packages/db/drizzle" \
CADCAMFUN_AI_HOST=direct CADCAMFUN_AI_OFFLINE=true \
bun --no-env-file apps/sidecar/src/main.ts
# stdout: {"event":"ready","port":43461}

The port is chosen by the operating system on every start. The line is the first stdout output after migrations and layers are ready, so a client that sees it can call the API immediately. Keep stdin open: closing it is the shutdown signal.

In 3D

Sidecar process topology Drag to orbit · scroll to zoom · right-drag to pan
Processes at run time. The Tauri shell spawns the sidecar and waits for its {”event”:”ready”,”port”:N} line on stdout (sidecar.rs); the webview then talks HTTP to 127.0.0.1 on that port with the bearer token. The sidecar owns the SQLite database, the embedded nikcli AI host (CADCAMFUN_AI_HOST=nikcli, the default) and plugins; the mobile app reaches it through companion pairing.