Skip to content

Dev Setup

This guide gets you from a fresh clone to a running Haptique stack (server + mobile app + wiki) in under 10 minutes.


Prerequisites

Before starting, make sure you have the following installed:

  • Node.js (LTS — check .nvmrc or engines in root package.json for the pinned version)
  • npm (comes with Node.js)
  • Git
  • Xcode + iOS Simulator (macOS — for running the mobile app on an iOS simulator)
  • Android Studio (for Android emulator — optional if using iOS)
  • Expo Go (install on a physical device if you prefer not to use a simulator)

Step 1: Clone and Install

bash
git clone <repo-url>
cd nfs_haptique
npm install

npm install at the repo root installs dependencies for all workspaces in the monorepo: apps/server, apps/mobile, packages/shared, and sdk. This is a single-command install for the entire stack.


Step 2: Configure Environment Variables

Copy the example env file to create your local env:

bash
cp apps/server/.env.example apps/server/.env

Then open apps/server/.env and fill in the required values below.

Never commit your .env file. It is already in .gitignore.

Required env vars (minimum for local dev)

These must be set before the server will start correctly:

VariableExample ValueWhy
APP_NAMECantataApplication display name
APP_SECRET_KEYAny random stringSession signing key
ENCRYPTION_KEYAny random stringData encryption key
APP_URLhttp://localhost:8080Server base URL (used in redirects and links)
PORT8080HTTP server listen port
LICENSE_BYPASStrueSkip license validation in dev (required — see pitfalls)
LICENSE_PRODUCT_CODEHAPTIQUE_OSProduct identifier for license check
APP_DEBUGtrueEnable verbose debug logging
SELF_HOSTEDtrueRequired for OS UI to render (see pitfalls)
DB_BACKENDsqliteDatabase backend — use sqlite for local dev (no setup needed)
ADMIN_AUTH_BYPASStrueSkip admin authentication in dev
JWT_SECRET_KEYAny random stringJWT access token signing key
JWT_EXPIRATION1dAccess token TTL
JWT_REFRESH_SECRET_KEYAny random stringJWT refresh token signing key
JWT_REFRESH_EXPIRATION7dRefresh token TTL

Optional env vars (for feature testing)

VariableValueWhy
DRIVER_WS_PORT(blank)Separate port for the driver WebSocket server — defaults to PORT if not set
HOS_NODE_BINARY(blank)Optional Node executable for OS-local JavaScript drivers; defaults to process.execPath or node
FIREBASE_ENABLEDfalseDisable push notifications (skip Firebase setup)
S3_ENABLEDfalseDisable S3 media storage
MAIL_ENABLEDfalseDisable email sending
DEVICE_MOCKtrueRun mock devices without real hardware

Step 3: Start the Server

bash
# From repo root:
npm run server

# Equivalent:
npm run dev --workspace=apps/server

# Under the hood this runs:
# ts-node-dev --respawn --transpile-only -r tsconfig-paths/register index.ts

The server starts at http://localhost:8080. You should see console output confirming:

  • Express HTTP server is listening on the configured PORT
  • Driver WebSocket server is ready (same port unless DRIVER_WS_PORT is set separately)
  • SQLite database is initialized (file created automatically — no external DB setup needed)

The SQLite database file is created at apps/server/src/storage/. To specify a custom path, set DB_PATH in .env.


Step 4: Start the Mobile App

bash
# From repo root:
npm run mobile

# Equivalent:
npm run start --workspace=apps/mobile

# Under the hood this runs:
# expo start

After Expo CLI starts:

  • Press i to open in the iOS Simulator
  • Press a to open in the Android Emulator
  • Scan the QR code with the Expo Go app on a physical device

On the Discovery screen, enter the server URL:

Device typeURL to enter
iOS/Android Simulatorhttp://localhost:8080
Physical device (same network)http://<your-mac-lan-ip>:8080

To find your Mac's LAN IP:

bash
ifconfig | grep "inet " | grep -v 127.0.0.1

Step 5: Start the Wiki (optional)

bash
cd wiki
npm install   # first time only — wiki is a standalone package
npm run dev

The wiki dev server opens at http://localhost:5173 (VitePress default port).

To build the wiki for static deployment:

bash
cd wiki
npm run build

Output goes to wiki/docs/.vitepress/dist/.


Common Pitfalls

1. Missing .env in apps/server/

Symptom: Server starts but authentication fails silently, or routes return HTTP 500 errors.

Cause: The server loads dotenv at startup. If apps/server/.env does not exist, critical variables like APP_SECRET_KEY and JWT_SECRET_KEY are undefined — sessions cannot be signed and JWT auth fails for all requests.

Fix: Run cp apps/server/.env.example apps/server/.env and fill in the required values (Step 2 above).


2. SELF_HOSTED not set to true

Symptom: The root route (/) shows a "construction" or "coming soon" page. The OS web UI does not load.

Cause: The server checks SELF_HOSTED before rendering the OS web UI. Without it, a fallback placeholder page is served instead.

Fix: Set SELF_HOSTED=true in apps/server/.env.


3. LICENSE_BYPASS not set to true

Symptom: Every route redirects to /os/license. No content is reachable.

Cause: License validation middleware intercepts all requests before any route handler. Without a valid license or bypass flag, the entire server redirects to the license page.

Fix: Set LICENSE_BYPASS=true in apps/server/.env.


4. SQLite database location confusion

Note: DB_BACKEND=sqlite requires no external database. The SQLite file is created automatically when the server first starts.

Default location: apps/server/src/storage/

To customize: Set DB_PATH in .env to an absolute path for the SQLite file.


5. Mobile app cannot reach server on a physical device

Symptom: The Discovery screen times out or shows "Network request failed".

Cause: localhost resolves to the device itself on a physical phone — not to your Mac. This only works on simulators (which share the host network stack).

Fix: Use your Mac's LAN IP address instead of localhost when prompted in the app (e.g., http://192.168.1.50:8080). Find your IP with ifconfig | grep "inet " | grep -v 127.0.0.1.


6. DRIVER_WS_PORT confusion

Note: If DRIVER_WS_PORT is left blank (or not set), the driver WebSocket server shares the HTTP server port (PORT). A single port handles both HTTP and WebSocket driver connections.

If you set DRIVER_WS_PORT to a different value, external driver processes must connect to that specific port. The sdk/ sample drivers pick up this value from process.env.DRIVER_WS_PORT.


7. JavaScript driver runtime unavailable

Symptom: A local .js driver package uploads but fails to start.

Cause: HOS starts JavaScript drivers through JavaScriptDriverProcess, which needs a Node-compatible executable. In dev this normally resolves to process.execPath or node; packaged Electron builds may need an explicit runtime path.

Fix: Set HOS_NODE_BINARY to a valid Node executable. In Electron packaging, the runtime sets ELECTRON_RUN_AS_NODE=1 when needed so the bundled executable can run driver JavaScript.


Useful Commands

CommandRun fromWhat it does
npm run serverRepo rootStart HOS server in dev mode (hot-reload)
npm run mobileRepo rootStart Expo mobile app (choose simulator or device)
cd wiki && npm run devRepo rootStart VitePress wiki dev server
npm run build --workspace=apps/serverRepo rootBuild server TypeScript for production
npm run build --workspace=apps/mcp-driver-builderRepo rootBuild the AI Driver Builder MCP server
npm run test --workspace=apps/mcp-driver-builderRepo rootRun MCP driver-builder tests
cd wiki && npm run buildRepo rootBuild wiki static site
npm installRepo rootInstall/sync all workspace dependencies

Cross-References

  • Monorepo Map — directory-by-directory breakdown of the entire repo
  • Server Internals — how the server's request pipeline, middleware, and driver WebSocket work
  • API Reference — endpoint reference for testing the server manually once it is running