Spaces:
Sleeping
Run as a GitHub Codespace
Browse filesA third home for the same image, alongside Hugging Face Spaces and Railway, and
the only one that needs no account the user does not already have.
A dev container starts with its entrypoint overridden, so the image's ENTRYPOINT
never runs and the server has to be started explicitly. That happens in
postStartCommand rather than postCreateCommand because codespaces stop after 30
minutes idle: only postStartCommand runs again on resume, which is what makes this
behave like a server instead of a dev box to be restarted by hand.
The port is declared public in portsAttributes because there is no REST endpoint
for port visibility β a client that provisions codespaces cannot otherwise expose
anything without a human toggling it in the UI. Public is only safe here because
entrypoint.sh refuses to start without OPENCODE_SERVER_PASSWORD; every route on
this server runs shell commands, so a missing password has to fail loudly rather
than publish an open shell.
State goes under /workspaces rather than /data, which does not exist in a
codespace, so sessions and code survive an idle stop and are lost only when the
codespace itself is deleted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- .devcontainer/devcontainer.json +58 -0
- README.md +30 -1
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// opencode-cloud as a GitHub Codespace β a third home alongside Hugging Face Spaces and
|
| 2 |
+
// Railway.
|
| 3 |
+
//
|
| 4 |
+
// The same Dockerfile serves all three. What differs here is that a dev container starts with
|
| 5 |
+
// its entrypoint overridden, so the image's ENTRYPOINT never runs: the server has to be
|
| 6 |
+
// started explicitly, and started again after every idle stop.
|
| 7 |
+
//
|
| 8 |
+
// Codespaces stop after 30 minutes idle by default. That is fine for this use β a stopped
|
| 9 |
+
// codespace keeps its disk and restarts under the same name and URL β but it does mean
|
| 10 |
+
// postStartCommand, not postCreateCommand, is what boots the server, because only the former
|
| 11 |
+
// runs on every resume.
|
| 12 |
+
{
|
| 13 |
+
"name": "opencode-cloud",
|
| 14 |
+
"build": {
|
| 15 |
+
"dockerfile": "../Dockerfile"
|
| 16 |
+
},
|
| 17 |
+
|
| 18 |
+
// uid 1000, matching the image. The Dockerfile creates nothing else.
|
| 19 |
+
"remoteUser": "node",
|
| 20 |
+
|
| 21 |
+
// Hugging Face has no $PORT and falls back to 7860; keep the same here so one number
|
| 22 |
+
// describes the server everywhere.
|
| 23 |
+
"containerEnv": {
|
| 24 |
+
"PORT": "7860",
|
| 25 |
+
// entrypoint.sh persists sessions, logins and code under this root when it is writable.
|
| 26 |
+
// /data does not exist in a codespace, but /workspaces survives stop/start, so state
|
| 27 |
+
// outlives an idle timeout and is lost only when the codespace itself is deleted.
|
| 28 |
+
"OPENCODE_STATE_ROOT": "/workspaces/.opencode-state"
|
| 29 |
+
},
|
| 30 |
+
|
| 31 |
+
"forwardPorts": [7860],
|
| 32 |
+
"portsAttributes": {
|
| 33 |
+
"7860": {
|
| 34 |
+
"label": "opencode",
|
| 35 |
+
// The only way to expose a port without a human toggling it: there is no REST endpoint
|
| 36 |
+
// for port visibility, so a client that provisions codespaces has to declare it here.
|
| 37 |
+
//
|
| 38 |
+
// Public is safe *because* entrypoint.sh refuses to start without
|
| 39 |
+
// OPENCODE_SERVER_PASSWORD. Every route on this server runs shell commands, so basic
|
| 40 |
+
// auth is the only thing in front of it; a missing password fails loudly rather than
|
| 41 |
+
// publishing an open shell.
|
| 42 |
+
"visibility": "public",
|
| 43 |
+
"onAutoForward": "silent"
|
| 44 |
+
}
|
| 45 |
+
},
|
| 46 |
+
|
| 47 |
+
// Runs on create *and* on every resume from an idle stop, which is what makes the codespace
|
| 48 |
+
// behave like a server rather than a dev box you have to log into and restart by hand.
|
| 49 |
+
// Backgrounded so the codespace can finish starting; the log is where a refused boot
|
| 50 |
+
// (usually a missing password) explains itself.
|
| 51 |
+
"postStartCommand": "nohup /home/node/entrypoint.sh > /tmp/opencode.log 2>&1 & echo started",
|
| 52 |
+
|
| 53 |
+
"customizations": {
|
| 54 |
+
"vscode": {
|
| 55 |
+
"extensions": []
|
| 56 |
+
}
|
| 57 |
+
}
|
| 58 |
+
}
|
|
@@ -74,6 +74,35 @@ with mount path `/data` for persistence. Details and caveats: [docs/RAILWAY.md](
|
|
| 74 |
|
| 75 |
---
|
| 76 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
## Keeping the Space in sync with GitHub
|
| 78 |
|
| 79 |
`.github/workflows/sync-to-hf-space.yml` builds the image, boots it, checks that
|
|
@@ -150,7 +179,7 @@ removed and nothing advertises the capability.
|
|
| 150 |
| `OPENCODE_SERVER_PASSWORD` | β | **Required.** Basic auth password. |
|
| 151 |
| `OPENCODE_SERVER_USERNAME` | `opencode` | Basic auth username. |
|
| 152 |
| `PORT` | `7860` | Listen port. Railway sets this; HF must match `app_port`. |
|
| 153 |
-
| `OPENCODE_STATE_ROOT` | `/data` | Where to look for a writable volume. |
|
| 154 |
| `OPENCODE_WORKSPACE` | `$STATE_ROOT/workspace` or `$HOME/workspace` | Directory to serve. |
|
| 155 |
| `OPENCODE_CORS_ORIGINS` | β | Comma-separated extra CORS origins. |
|
| 156 |
| `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. |
|
|
|
|
| 74 |
|
| 75 |
---
|
| 76 |
|
| 77 |
+
## Run as a GitHub Codespace
|
| 78 |
+
|
| 79 |
+
```
|
| 80 |
+
Code -> Codespaces -> Create codespace on main
|
| 81 |
+
```
|
| 82 |
+
|
| 83 |
+
Set `OPENCODE_SERVER_PASSWORD` first, as a **Codespaces** secret β repository secrets
|
| 84 |
+
and Actions secrets are separate and are not visible here:
|
| 85 |
+
|
| 86 |
+
```bash
|
| 87 |
+
gh secret set OPENCODE_SERVER_PASSWORD --app codespaces --repo OWNER/REPO
|
| 88 |
+
```
|
| 89 |
+
|
| 90 |
+
`.devcontainer/devcontainer.json` builds the same Dockerfile, forwards 7860 publicly and
|
| 91 |
+
starts the server on `postStartCommand`. Public is safe only because `entrypoint.sh` refuses
|
| 92 |
+
to start without a password; without the secret the codespace comes up with no server and the
|
| 93 |
+
reason is in `/tmp/opencode.log`.
|
| 94 |
+
|
| 95 |
+
Unlike a Space or a Railway service, a codespace **stops after 30 minutes idle** (240 maximum).
|
| 96 |
+
It keeps its disk and restarts under the same name and URL, and `postStartCommand` boots the
|
| 97 |
+
server again β but it is not always-on, and GitHub deletes a stopped codespace after the
|
| 98 |
+
retention period, up to 30 days. State lives under `/workspaces/.opencode-state`, so it
|
| 99 |
+
survives a stop and is lost only with the codespace itself.
|
| 100 |
+
|
| 101 |
+
A personal Free account includes 120 core-hours a month, which is roughly 60 hours of wall
|
| 102 |
+
clock on a 2-core machine; the idle timeout is what keeps that from draining.
|
| 103 |
+
|
| 104 |
+
---
|
| 105 |
+
|
| 106 |
## Keeping the Space in sync with GitHub
|
| 107 |
|
| 108 |
`.github/workflows/sync-to-hf-space.yml` builds the image, boots it, checks that
|
|
|
|
| 179 |
| `OPENCODE_SERVER_PASSWORD` | β | **Required.** Basic auth password. |
|
| 180 |
| `OPENCODE_SERVER_USERNAME` | `opencode` | Basic auth username. |
|
| 181 |
| `PORT` | `7860` | Listen port. Railway sets this; HF must match `app_port`. |
|
| 182 |
+
| `OPENCODE_STATE_ROOT` | `/data` | Where to look for a writable volume. Codespaces sets `/workspaces/.opencode-state`. |
|
| 183 |
| `OPENCODE_WORKSPACE` | `$STATE_ROOT/workspace` or `$HOME/workspace` | Directory to serve. |
|
| 184 |
| `OPENCODE_CORS_ORIGINS` | β | Comma-separated extra CORS origins. |
|
| 185 |
| `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. |
|