Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
6f310a2
feat(fork): decouple from CopilotKit hosted services and harden for s…
KayzenRoot Nov 5, 2026
e94e9c3
docs(checkpoint): record verified status, evidence and remaining bloc…
KayzenRoot Nov 5, 2026
2087e78
docs(checkpoint): record live Slack end-to-end verification
KayzenRoot Nov 5, 2026
70eab56
fix(restore): grant DAC_OVERRIDE/CHOWN and chmod before chown
KayzenRoot Nov 5, 2026
5b23a9e
fix(deps): remediate redundant DoS and sanitiser advisories
KayzenRoot Nov 5, 2026
a62c376
docs(audit): security review and backup/restore round-trip evidence
KayzenRoot Nov 5, 2026
feec4f4
feat(install): local Windows test installation with proxy and autostart
KayzenRoot Nov 5, 2026
1ccd433
feat(install): native desktop launcher and one-click installer
KayzenRoot Nov 5, 2026
2e009c4
feat(installer): self-extracting Setup.exe for customer machines
KayzenRoot Nov 5, 2026
724c549
fix(ui): stop clipping the Spaces list in the sidebar
KayzenRoot Nov 5, 2026
7f201fb
feat(ui): glass surfaces, living mascot and a larger hero in dark mode
KayzenRoot Nov 5, 2026
65c2a1a
feat(ui): give the dark canvas depth so the glass surfaces read
KayzenRoot Nov 5, 2026
dfde3cb
feat(config): in-app model provider setup with sealed API key
KayzenRoot Nov 6, 2026
afb714c
fix(config): forward gateway headers when probing, and stop ambient e…
KayzenRoot Nov 6, 2026
46fbcd3
feat(ops): health watchdog that restarts the stack after a real outage
KayzenRoot Nov 6, 2026
af8e5c9
test(security): assert the posture the operator depends on
KayzenRoot Nov 6, 2026
d99ddc2
docs: production readiness gate list, and flag the stale upstream guide
KayzenRoot Nov 6, 2026
2efe082
feat(ui): model provider form in Settings
KayzenRoot Nov 6, 2026
c0b229f
feat(brand): rename the product to NexBot and add the identity
KayzenRoot Nov 6, 2026
b587779
docs: point fork references at the renamed repository
KayzenRoot Nov 6, 2026
3c80a75
feat(security): rate-limit the public webhook and token guessing
KayzenRoot Nov 6, 2026
407e99d
feat: customer guide, configurable voice endpoint, and boxed-off rema…
KayzenRoot Nov 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 104 additions & 46 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,77 +1,135 @@
# OpenDots template: the UI opens in setup state until these fields are configured.
HOST=127.0.0.1
# =============================================================================
# NexBot — decoupled, self-hosted fork: environment template
# =============================================================================
# Copy this file to `.env` and fill in the required values. Never commit `.env`.
#
# NO CopilotKit account, project, organization, or API key is needed.
# Conversations are stored in your own PostgreSQL; non-conversation state
# (dots, spaces, pages, tasks, connections, computers) stays in SQLite. The
# app talks only to the model endpoint you choose. Nothing is sent to
# CopilotKit, and no CopilotKit telemetry variable is used.
#
# Generate secrets (each 24+ characters):
# openssl rand -base64 48
#
# Values marked REQUIRED have no safe default: Compose refuses to start without
# them (it uses ${VAR:?} for the required app/postgres variables).
# =============================================================================

# =============================================================================
# REQUIRED
# =============================================================================

# --- Application binding -----------------------------------------------------
# HOST/PORT the server listens on inside the container. Keep 0.0.0.0 so the
# container is reachable; the published port stays bound to host loopback.
HOST=0.0.0.0
PORT=4310
DATABASE_PATH=data/opendots.sqlite
# Keep OWNER_ID stable: it owns the persistent Intelligence conversations.
OWNER_ID=opendots-owner
# npm run dev allows http://localhost:5173 and http://127.0.0.1:5173.
# APP_ORIGIN replaces these defaults with comma-separated exact origins.
# Include scheme, hostname and port; omit paths and trailing slashes.
# APP_ORIGIN=http://localhost:5173,http://127.0.0.1:5173
# Required for an external HOST binding (24+ characters); enables local login too.
# OWNER_TOKEN=
# REQUIRED when HOST is not loopback (24+ characters). Bearer token for the
# owner login and every /api request. Treat it like a password.
OWNER_TOKEN=
# Exact browser origin(s) allowed to call the API, comma-separated. Include
# scheme, hostname and port; omit paths and trailing slashes.
# Behind a reverse proxy set this to your public HTTPS origin, e.g.:
# APP_ORIGIN=https://dots.example.com
# Whitespace around entries is trimmed and empty entries are ignored.
# APP_ORIGIN=https://dots.example.com

# --- Owner identity ----------------------------------------------------------
# Keep OWNER_ID stable: it owns the persistent conversations and other
# owner-scoped state. Changing it after first use orphans that data.
OWNER_ID=nexbot-owner

# --- Non-conversation state: SQLite (REQUIRED) -------------------------------
# File holding dots, spaces, pages, tasks, connections, computers and thread
# bindings. In the container this is a path on the `nexbot-data` volume.
DATABASE_PATH=/data/nexbot.sqlite

# `copilotkit project select` or `local connect` writes CPK_INTELLIGENCE_API_KEY.
# The server accepts either name and prefers a non-empty CPK_INTELLIGENCE_API_KEY.
INTELLIGENCE_API_KEY=
# INTELLIGENCE_API_URL=
# `local connect` writes this endpoint; it takes precedence over the legacy name.
# INTELLIGENCE_GATEWAY_WS_URL=
# INTELLIGENCE_WS_URL=
# --- Conversations: PostgreSQL (REQUIRED) ------------------------------------
# REQUIRED. Replaces the hosted CopilotKit conversation service.
# Compose-internal form (the host is the bundled postgres service name):
# postgres://<POSTGRES_USER>:<POSTGRES_PASSWORD>@postgres:5432/<POSTGRES_DB>
# With the values below that is:
# postgres://nexbot:<POSTGRES_PASSWORD>@postgres:5432/nexbot
# Keep DATABASE_URL consistent with the POSTGRES_* values below.
DATABASE_URL=
# Credentials for the bundled `postgres` service in compose.yml.
# It publishes NO host port; it is reachable only on the internal network.
POSTGRES_USER=nexbot
# REQUIRED. Generate one: openssl rand -base64 48
POSTGRES_PASSWORD=
POSTGRES_DB=nexbot

# --- Model endpoint (REQUIRED for agent runs) --------------------------------
# Any OpenAI-compatible endpoint. Point OPENAI_BASE_URL at a self-hosted server
# (Ollama, vLLM, LM Studio) or a hosted provider (OpenAI, OpenRouter, ...).
OPENAI_API_KEY=
OPENAI_BASE_URL=https://api.openai.com/v1
OPENAI_MODEL=
# Maximum duration for Dot turns and scheduled/background agent runs (milliseconds).
# Extra headers sent to the model endpoint, as a JSON object of strings. Some
# gateways require routing headers. OpenCode Go needs a session header:
# OPENAI_EXTRA_HEADERS='{"x-opencode-session":"nexbot"}'
# OPENAI_EXTRA_HEADERS=
# Maximum duration for Dot turns and scheduled/background agent runs (ms).
# AGENT_RUN_TIMEOUT_MS=90000
# Keep the CLI-generated CPK_TELEMETRY_ID with its matching project key.
# The SDK uses it to connect OpenDots usage to the Intelligence account.
# CPK_TELEMETRY_ID=
# SDK usage telemetry is enabled by default (separate from conversations).
# See docs/TELEMETRY.md for signup reporting and validation.
# Optional sampling override (default 1):
# COPILOTKIT_TELEMETRY_SAMPLE_RATE=1
# Either flag below opts out:
# COPILOTKIT_TELEMETRY_DISABLED=true
# DO_NOT_TRACK=1

# Optional isolated read-only public-page browser for WEB_SEARCH_PROVIDER=browser.
# =============================================================================
# OPTIONAL
# =============================================================================

# --- Setup telemetry ---------------------------------------------------------
# OPTIONAL, unset by default. When unset, NO telemetry leaves the host.
# If set, anonymous setup/version telemetry is POSTed to this URL.
# SETUP_TELEMETRY_ENDPOINT=

# --- Web search --------------------------------------------------------------
# parallel (default), browser (URL-only reader), or disabled.
# With `parallel`, queries and selected URLs are sent to Parallel; set a key
# for authenticated/higher-limit use (anonymous MCP supports light use).
WEB_SEARCH_PROVIDER=parallel
PARALLEL_API_KEY=

# --- Isolated read-only browser (needed only for WEB_SEARCH_PROVIDER=browser) -
# Supply the same random 24+ character secret to the app and browser service.
# All redirects are blocked; use canonical public URLs.
BROWSER_URL=http://127.0.0.1:4311
BROWSER_SECRET=
BROWSER_HOST=127.0.0.1
BROWSER_PORT=4311
# Use the same random 24+ character secret in app/browser. All redirects are blocked.
# Supply canonical public URLs. Use the separate browser container for isolation.

# Optional connected apps through Composio; see docs/SETUP.md#composio.
# --- Composio connected apps -------------------------------------------------
# Lets Dots use the owner's connected apps. See docs/SETUP.md#composio.
COMPOSIO_API_KEY=

# Optional realtime speech; compute uses the same configured Intelligence thread.
# --- Realtime speech ---------------------------------------------------------
VOICE_API_KEY=
# Realtime endpoint root, for a non-OpenAI gateway (default https://api.openai.com).
# VOICE_BASE_URL=
VOICE_MODEL=
VOICE_NAME=marin

# Optional managed Slack channel; see docs/SETUP.md#slack.
# --- Slack (NOT YET WIRED) ---------------------------------------------------
# OPTIONAL. SLACK_BOT_TOKEN and SLACK_SIGNING_SECRET come from a self-hosted
# Slack integration that replaces @copilotkit/channels in a LATER workstream.
# This build does not read them yet; setting them has no effect today.
# SLACK_BOT_TOKEN=
# SLACK_SIGNING_SECRET=
# The managed vars below are still accepted by the current build.
# Match the managed declaration name, not a Slack #conversation name.
# OpenTag calls this INTELLIGENCE_CHANNEL_NAME; OpenDots uses SLACK_CHANNEL_NAME.
# Allowlist both the Slack workspace and explicit Slack user IDs.
SLACK_CHANNEL_NAME=
SLACK_TEAM_ID=
SLACK_USER_IDS=
# SLACK_DOT_ID= (defaults to the initial Dot)

# Optional persistent computers, one per Dot, using OpenBot services.
# See docs/COMPUTERS.md. Keep these two different random secrets on the server.
# --- Persistent computers (OpenBot supervisor) -------------------------------
# OPTIONAL, one container per Dot. See docs/COMPUTERS.md.
# Keep these two DIFFERENT random 24+ character secrets on the server.
COMPUTER_SUPERVISOR_URL=
COMPUTER_SUPERVISOR_TOKEN=
COMPUTER_TOKEN=
# Keep stable: the namespace identifies existing containers and persistent volumes.
COMPUTER_NAMESPACE=opendots
# Keep stable: the namespace identifies existing containers and volumes.
COMPUTER_NAMESPACE=nexbot
# COMPUTER_MEMORY_BYTES=2147483648
# COMPUTER_RUNTIME=runsc
# ENGINE_SOCKET=/var/run/docker.sock

# Public-web provider. parallel (default), browser (URL-only), or disabled.
# Queries and selected URLs are sent to Parallel; sample mode makes no provider calls.
WEB_SEARCH_PROVIDER=parallel
# Optional Bearer key for production/higher limits; anonymous MCP supports light use.
PARALLEL_API_KEY=
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ dist/
.env
.env.*
!.env.example
.opendots/
.nexbot/
artifacts/
playwright-report/
test-results/
Expand All @@ -18,3 +18,7 @@ data/

# Local hosted-project selection
.copilotkit/

# Compiled launcher (build with deployment/windows/build-launcher.cmd)
deployment/windows/NexBot.exe
logs/
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Contributing to OpenDots
# Contributing to NexBot

OpenDots is an application template in early development. Focus changes on Spaces, Specialist Dots, text and calls, Slack, and inspectable background work. Keep the documented SDK integrations functional and report missing configuration clearly.
NexBot is an application template in early development. Focus changes on Spaces, Specialist Dots, text and calls, Slack, and inspectable background work. Keep the documented SDK integrations functional and report missing configuration clearly.

For bugs, include the app mode, Node version, steps to reproduce, expected behavior, and actual behavior. Remove credentials and private page content from logs or screenshots.

Expand Down
13 changes: 12 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,22 @@ COPY . .
RUN npm run build

FROM node:24-bookworm-slim AS app
ENV NODE_ENV=production HOST=0.0.0.0 PORT=4310 DATABASE_PATH=/data/opendots.sqlite
ENV NODE_ENV=production HOST=0.0.0.0 PORT=4310 DATABASE_PATH=/data/nexbot.sqlite
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev && mkdir -p /data && chown node:node /data
COPY --from=build /app/dist ./dist
# Entrypoint validates config, waits (bounded) for Postgres, then execs the app.
# The app runs its idempotent, versioned migrations on startup and exits
# non-zero if they cannot run; exec preserves that exit code.
COPY deployment/docker-entrypoint.sh /usr/local/bin/nexbot-entrypoint.sh
RUN chmod 0755 /usr/local/bin/nexbot-entrypoint.sh
USER node
EXPOSE 4310
# Liveness only (no datastore access, no auth). Node 24 has global fetch.
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
CMD node -e 'fetch("http://127.0.0.1:4310/api/health").then((r) => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))'
ENTRYPOINT ["/usr/local/bin/nexbot-entrypoint.sh"]
CMD ["node", "dist/server/server/index.js"]

FROM node:24-bookworm-slim AS browser
Expand All @@ -23,4 +32,6 @@ RUN npm ci --omit=dev && npx playwright install --with-deps chromium && chmod -R
COPY --from=build /app/dist/server ./dist/server
USER node
EXPOSE 4311
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
CMD node -e 'fetch("http://127.0.0.1:4311/health").then((r) => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))'
CMD ["node", "dist/server/browser/index.js"]
Loading