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.
{"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:
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_URLsets the base for authentication links;CADCAMFUN_DEBUG_LINKSenables returning debug reset/magic links and defaults to false.CADCAMFUN_AI_HOSTisnikcli(default; chat runs through the embedded conversation host) ordirect(AI SDK only).CADCAMFUN_AI_OFFLINE(boolean, default false) disables the optional model-catalogue network path.CADCAMFUN_AI_MINIMAX_BASE_URLoverrides MiniMax's endpoint.STRIPE_SECRET_KEYenables the Stripe provider;STRIPE_PRICE_BASIC,STRIPE_PRICE_PRO, andSTRIPE_PRICE_ENTERPRISEsupply paid-plan prices.STRIPE_WEBHOOK_SECRETis required for signed webhook verification.CRON_SECRETsupplies the cron route secret; without it the local application can call that route without an additional cron secret.ADMIN_EMAILis a legacy administrator identity check; by default the local profile is the administrator of its own installation.OTEL_EXPORTER_OTLP_ENDPOINTenables 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.
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:
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
| Symptom | Likely boundary and check |
|---|---|
| Process exits before ready | Read the protocol error and stderr. Check required config, directory access, migration identity, and service construction. |
| Health answers but protected routes return 401 | Use 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 browser | Inspect 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 launch | Check 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 enable | Read 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 another | Compare 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
- 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, andCADCAMFUN_MIGRATIONS_DIR. AppConfigServicereads all settings once. A missing required setting fails startup with anerrorevent.- The database layer opens
<dataDir>/cadcamfun.db, enables WAL mode and foreign keys, and applies migrations from the migrations directory. - 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.
- The HTTP server binds
127.0.0.1on port 0, and the operating system picks a free port. - The public health route is probed. Only after it answers is
{"event":"ready","port":N}written to stdout. - 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.
| Stream | Carries | Consumer |
|---|---|---|
| stdout | Exactly one ready or error JSON line | The parent process (shell or your script). |
| stderr | JSON operational logs | Log 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 down | The sidecar's shutdown reader. Oversized or malformed lines are ignored. |
Authentication model
| Credential | Where it comes from | What it opens |
|---|---|---|
Launch token (Authorization: Bearer) | Generated by the shell per launch, or set by you for manual runs | Every protected route of the loopback API. |
Session token (x-session-token) | Sign-in flows; signed by a per-installation secret stored next to the database | Selects the acting user. If missing or invalid, requests act as local@cadcamfun.local. |
| Bridge token | POST /api/ai/bridge/tokens; shown once | Only the action listing and invocation routes, for one project, read or read-write, until expiry (default 30 days, maximum 365). |
| Companion device token | Pairing; shown once inside the pairing link; stored only as a SHA-256 hash | The 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
| Path | Content |
|---|---|
cadcamfun.db (+ -wal, -shm) | SQLite database in WAL mode. |
auth-secret | Per-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 storage | Uploaded 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
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.