Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Built with CopilotKit React Native for iOS, Android, and web.
[![CI](https://github.com/CopilotKit/OpenMuse/actions/workflows/ci.yml/badge.svg)](https://github.com/CopilotKit/OpenMuse/actions/workflows/ci.yml)
[![MIT license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/CopilotKit/OpenMuse)

Clone this template and customize it however you want.

**[Building on OpenMuse? Meet with the CopilotKit team →](https://www.copilotkit.ai/openmuse)**
Expand Down Expand Up @@ -91,6 +93,48 @@ Open [localhost:8081](http://localhost:8081). The API runs at [localhost:8787/ap

For iOS or Android, use `pnpm --dir apps/mobile ios` or `pnpm --dir apps/mobile android`. Xcode or Android tooling is required. The PDF reader needs an Expo development build; use [native setup](apps/mobile/README.md).

## Deploy on Render

[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/CopilotKit/OpenMuse)

[render.yaml](render.yaml) deploys three services: the API, the web app, and a private browser. The API answers `/` with JSON, so the UI is its own static site.

### First run

1. Click **Deploy to Render**. Wait until `openmuse-api`, `openmuse-web`, and `openmuse-browser` are live.
2. On `openmuse-api`, open **Environment** and copy `OPENMUSE_ACCESS_KEY`.
3. Open the `openmuse-web` URL and sign in with that key.
4. Send a message.

The deploy form asks for two values you provide. Render generates the other two.

| Variable | Set by | If it is missing |
|---|---|---|
| `CPK_INTELLIGENCE_API_KEY` | You. Run `npx copilotkit@latest login`, then `npx copilotkit@latest project select`. Keep it on the server. | Chat cannot open a thread. |
| `OPENAI_API_KEY` | You. Used by the default `openai/gpt-5`. Change `MODEL` and supply the matching provider key for Anthropic or Google. | The model call fails. |
| `OPENMUSE_ACCESS_KEY` | Render | You cannot sign in. |
| `TOKEN_ENCRYPTION_KEY` | Render | The API refuses to start in live mode. |

Health check: `https://<openmuse-api>/api/health`.

### Services

| Service | Plan | What it runs |
|---|---|---|
| `openmuse-api` | Standard, with a 1 GB disk at `/var/data` | The Hono API and the in-process task worker. `DATA_DIR` is `/var/data/openmuse`. |
| `openmuse-web` | Static site | The Expo web export. `EXPO_PUBLIC_API_URL` is baked in at build time. |
| `openmuse-browser` | Private service, Standard, 1 GB disk at `/data` | Playwright and Chromium. The API calls it on the private network. |

**Standard** is the smallest plan that stays up. At 512 MB the process runs out of memory before it binds a port, because PGlite loads an embedded Postgres build.

**The disk** holds the database, PDFs, and the signing key. A redeploy without it wipes that data. Chat threads are stored by CopilotKit Intelligence, so a thread can still load after you sign back in even when the disk was never attached.

**Live mode** is required. Render binds `0.0.0.0`, and sample mode rejects any host that is not loopback. The Blueprint sets `WORKSPACE_MODE=live`.

**Browsing is included, and you can take it out.** `openmuse-browser` is a private service, so it has no public URL. The API reaches it at `http://openmuse-browser:8790` with a token Render generates. If the private hostname is not `openmuse-browser`, set `BROWSER_WORKER_URL` to `http://<that-host>:8790`. To deploy without it, delete the `openmuse-browser` service and the `BROWSER_WORKER_URL` and `WORKER_TOKEN` entries on `openmuse-api`. Chat, drafts, and tasks still run. Page reads, screenshots, and **Take control** do not.

The Docker computer and Google mail or calendar need the setup in the sections below. This Blueprint does not start them.

## Configure the agent and Google

Copy the commented settings in [.env.example](.env.example) into your private `.env`:
Expand Down
97 changes: 97 additions & 0 deletions render.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
services:
- type: web
name: openmuse-api
runtime: node
# starter (512 MB) gets OOM-killed before the server binds a port, because
# the default PGlite store loads an embedded Postgres WASM build.
plan: standard
# No corepack: Render preinstalls pnpm at /usr/bin and /usr is read-only, so
# `corepack enable` dies with EROFS. Render's pnpm honors packageManager.
buildCommand: pnpm install --frozen-lockfile && pnpm build:server
startCommand: pnpm start
healthCheckPath: /api/health
disk:
name: openmuse-data
mountPath: /var/data
sizeGB: 1
envVars:
# HOST must be 0.0.0.0 on Render, and OpenMuse only allows that in live mode.
- key: HOST
value: 0.0.0.0
- key: WORKSPACE_MODE
value: live
- key: AGENT_BACKEND
value: model
- key: MODEL
value: openai/gpt-5
- key: DATA_DIR
value: /var/data/openmuse
- key: TASK_WORKER_ENABLED
value: true
- key: PUBLIC_API_URL
fromService:
name: openmuse-api
type: web
envVarKey: RENDER_EXTERNAL_URL
- key: ALLOWED_ORIGINS
fromService:
name: openmuse-web
type: web
envVarKey: RENDER_EXTERNAL_URL
- key: OPENMUSE_ACCESS_KEY
generateValue: true
- key: TOKEN_ENCRYPTION_KEY
generateValue: true
- key: OPENAI_API_KEY
sync: false
- key: CPK_INTELLIGENCE_API_KEY
sync: false
# Delete these two entries, and the openmuse-browser service below, to
# deploy without browsing. The API boots either way.
- key: BROWSER_WORKER_URL
value: http://openmuse-browser:8790
- key: WORKER_TOKEN
fromService:
name: openmuse-browser
type: pserv
envVarKey: WORKER_TOKEN

# Private Chromium for page reads, screenshots, and Take control.
# Remove this service to skip it. The image listens on 8790, not $PORT.
- type: pserv
name: openmuse-browser
runtime: docker
plan: standard
dockerfilePath: ./apps/worker/Dockerfile
dockerContext: ./apps/worker
disk:
name: openmuse-browser-data
mountPath: /data
sizeGB: 1
envVars:
- key: WORKER_TOKEN
generateValue: true
- key: WORKER_HOST
value: 0.0.0.0
- key: WORKER_DATA_DIR
value: /data

- type: web
name: openmuse-web
runtime: static
# --clear avoids a cached Metro transform keeping a stale EXPO_PUBLIC_API_URL,
# which Expo inlines into the bundle at build time.
buildCommand: >-
pnpm install --frozen-lockfile &&
pnpm --dir apps/mobile exec expo export --platform web --output-dir dist/web --clear
staticPublishPath: apps/mobile/dist/web
routes:
- type: rewrite
source: /*
destination: /index.html
envVars:
- key: EXPO_PUBLIC_API_URL
fromService:
name: openmuse-api
type: web
envVarKey: RENDER_EXTERNAL_URL
Loading