opencode-cloud / README.md
polats's picture
Run as a GitHub Codespace
930c3ef
|
Raw
History Blame Contribute Delete
8.47 kB
metadata
title: OpenCode Cloud
emoji: πŸ€–
colorFrom: gray
colorTo: indigo
sdk: docker
app_port: 7860
pinned: false
license: mit

opencode-cloud

Run opencode β€” an open source AI coding agent β€” as a hosted server you reach from a browser, from the desktop app, or from a local build of the web app. One Dockerfile, deployable to Hugging Face Spaces or Railway.

opencode serve is not just an API: the server also serves the web UI on its catch-all route, so opening the deployment URL gives you the full app, same-origin.


Deploy on Hugging Face Spaces

  1. Create a Space β†’ SDK Docker, blank template. Push this repo to it (or duplicate an existing Space built from it).

  2. Settings β†’ Variables and secrets and add:

    Secret Required Notes
    OPENCODE_SERVER_PASSWORD yes The container refuses to start without it. This is the only thing protecting a shell.
    OPENCODE_SERVER_USERNAME no Defaults to opencode.
    OPENCODE_API_KEY pick β‰₯1 opencode zen curated models
    ANTHROPIC_API_KEY pick β‰₯1 https://console.anthropic.com
    OPENAI_API_KEY pick β‰₯1 https://platform.openai.com/api-keys
    GEMINI_API_KEY pick β‰₯1 https://aistudio.google.com/apikey
  3. Open the Space URL and log in with opencode + your password.

Provider keys work as plain env vars β€” opencode registers any provider whose models.dev env var is present, no login step needed.

Persistence

Free Spaces are ephemeral and are stopped after ~48h idle. Sessions, provider logins and any uncommitted code are lost on restart. Two options:

  • Free: git clone your repo into the workspace and push before you walk away.
  • Paid: enable persistent storage (Settings β†’ Storage). It mounts at /data, which entrypoint.sh detects and uses for the XDG dirs and the workspace, so state survives restarts. Watch the boot log β€” it prints which mode it picked.

Public or private Space?

A private Space adds your HF login in front of everything, which is the safer default for browser use. The catch: HF authenticates private Spaces with an Authorization: Bearer hf_… header, and opencode wants Authorization: Basic … β€” one header, two claimants. So a private Space works in a browser (HF session cookie) but a desktop-app or local-app connection to a private Space will fight over that header. Keep the Space public with a long password if you want those.


Deploy on Railway

railway init                       # or point a service at this GitHub repo
railway variables --set OPENCODE_SERVER_PASSWORD=...
railway up

Railway injects $PORT, which entrypoint.sh uses automatically. Attach a volume with mount path /data for persistence. Details and caveats: docs/RAILWAY.md.


Run as a GitHub Codespace

Code -> Codespaces -> Create codespace on main

Set OPENCODE_SERVER_PASSWORD first, as a Codespaces secret β€” repository secrets and Actions secrets are separate and are not visible here:

gh secret set OPENCODE_SERVER_PASSWORD --app codespaces --repo OWNER/REPO

.devcontainer/devcontainer.json builds the same Dockerfile, forwards 7860 publicly and starts the server on postStartCommand. Public is safe only because entrypoint.sh refuses to start without a password; without the secret the codespace comes up with no server and the reason is in /tmp/opencode.log.

Unlike a Space or a Railway service, a codespace stops after 30 minutes idle (240 maximum). It keeps its disk and restarts under the same name and URL, and postStartCommand boots the server again β€” but it is not always-on, and GitHub deletes a stopped codespace after the retention period, up to 30 days. State lives under /workspaces/.opencode-state, so it survives a stop and is lost only with the codespace itself.

A personal Free account includes 120 core-hours a month, which is roughly 60 hours of wall clock on a 2-core machine; the idle timeout is what keeps that from draining.


Keeping the Space in sync with GitHub

.github/workflows/sync-to-hf-space.yml builds the image, boots it, checks that it refuses to start unauthenticated and that it serves an authenticated API and the web UI β€” and only then force-pushes main to the Space and waits for it to report RUNNING. A broken Dockerfile fails on the runner instead of leaving the Space stuck in BUILD_ERROR.

Configure it under Settings β†’ Secrets and variables β†’ Actions:

Name Kind Notes
HF_TOKEN secret A Hugging Face write token. The HF username is derived from it, so it is the only secret needed.
HF_SPACE variable owner/space-name. Optional β€” defaults to this GitHub repo's owner/name. A public Space id isn't sensitive, so a variable keeps it readable in logs; a secret of the same name also works.

GitHub is the source of truth: the sync force-pushes, so a commit made only in the Space's web UI will be discarded. Edit here, not there.


Connecting the desktop app or a local web app

You don't have to use the in-browser UI. In the app, Settings β†’ Servers β†’ Add and enter the deployment URL plus the username/password. The server's CORS allowlist already covers localhost, *.opencode.ai and the desktop app's oc://renderer origin, so no extra flags are needed. Only a frontend you host on your own domain needs OPENCODE_CORS_ORIGINS=https://your.domain.


Giving the agent Hugging Face access

Set HF_TOKEN as a Space secret (or Railway variable) and the agent can create and manage Hugging Face repos: the official hf CLI is installed in the image, it picks the token up from the environment with no login step, and entrypoint.sh installs agents/hf-spaces.md as opencode's global AGENTS.md so the agent knows the recipes. With no HF_TOKEN set, that file is removed and nothing advertises the capability.

This hands your HF account to whoever can reach the server. A standard HF write token covers every repo the account can write to β€” including org repos β€” so the blast radius is much larger than "a shell in a container". Two things make it meaningfully safer:

  • Use a fine-grained token scoped to just the permissions you need (write access to Spaces, say) rather than an account-wide write token.
  • Remember the agent reads untrusted content β€” repos, web pages, issue text. Anything it reads can try to talk it into using the token. The instructions tell it never to print the token, but that is a speed bump, not a boundary.

Read this before you deploy

  • This is a remote shell. Every route allows command execution and file read/write in the container. Basic auth over HTTPS is the entire security model, so use a long random password, and think twice about what credentials you put in the container alongside it.
  • Provider OAuth "login with browser" flows don't work on a remote server. They bind a http://localhost:<port>/auth/callback listener inside the container, so your browser is redirected to your own machine instead. Use API keys.
  • CPU only, and modest. Fine for the agent; you can't run local models.
  • Check the host's terms. A general-purpose remote shell is not the ML-demo use case Spaces are described for; a public one with a weak password is the way to get flagged.

Environment variables

Variable Default Purpose
OPENCODE_SERVER_PASSWORD β€” Required. Basic auth password.
OPENCODE_SERVER_USERNAME opencode Basic auth username.
PORT 7860 Listen port. Railway sets this; HF must match app_port.
OPENCODE_STATE_ROOT /data Where to look for a writable volume. Codespaces sets /workspaces/.opencode-state.
OPENCODE_WORKSPACE $STATE_ROOT/workspace or $HOME/workspace Directory to serve.
OPENCODE_CORS_ORIGINS β€” Comma-separated extra CORS origins.
HF_TOKEN β€” Optional. A Hugging Face token, which lets the agent create and manage HF repos with the bundled hf CLI. See the warning below.

Build arg OPENCODE_VERSION pins a release (default: latest).


MIT. opencode itself is Β© Anomaly Innovations; this repo is deployment wrapper code.