diff --git a/README.md b/README.md index 1d4bd700e..6b662c45b 100644 --- a/README.md +++ b/README.md @@ -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)** @@ -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:///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://: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`: diff --git a/render.yaml b/render.yaml new file mode 100644 index 000000000..88ea05201 --- /dev/null +++ b/render.yaml @@ -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