Skip to content

Commit 84a0d9b

Browse files
pRizzclaude
andcommitted
feat(docker): add compose CI validation, security warnings, and DO docker-compose docs
Add docker-compose.yml schema validation to CI pipeline and local pre-commit checks. Add runtime security warnings in container logs for disabled auth and missing HTTPS on DigitalOcean deployments. Expand DigitalOcean Droplet docs with full Docker Compose deployment path and update README to reflect that persistence is now supported. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent ccbeab9 commit 84a0d9b

6 files changed

Lines changed: 182 additions & 22 deletions

File tree

.github/workflows/ci.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,15 @@ jobs:
5757
- name: Packer validate
5858
run: packer validate -var-file=infra/digitalocean/packer/variables.pkr.hcl infra/digitalocean/packer/opencode-marketplace.pkr.hcl
5959

60+
compose-validate:
61+
name: Compose File Validate
62+
runs-on: ubuntu-latest
63+
steps:
64+
- uses: actions/checkout@v6
65+
66+
- name: Validate docker-compose.yml
67+
run: docker compose config --quiet
68+
6069
docker-context-guard:
6170
name: Docker Context Guard
6271
runs-on: ubuntu-latest

README.md

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,31 @@ docker compose logs | grep -F "INITIAL ONE-TIME PASSWORD (IOTP): " | tail -n1 |
8282

8383
Docs: `docs/deploy/docker-desktop.md`
8484

85-
## Deploy to DigitalOcean (Coming Soon)
85+
## Deploy to DigitalOcean
8686

87-
DigitalOcean Marketplace one-click deployment is not implemented yet. Support is coming soon.
87+
### Marketplace (Coming Soon)
8888

89-
Warning: direct manual Droplet deployments are currently not recommended because persistence support is incomplete and data loss is likely.
89+
DigitalOcean Marketplace one-click deployment is in progress. Support is coming soon.
9090

91-
For testing-only reference:
92-
- Manual Droplet setup: `docs/deploy/digitalocean-droplet.md`
93-
- Marketplace docs/status: `docs/deploy/digitalocean-marketplace.md`
91+
Docs: `docs/deploy/digitalocean-marketplace.md`
92+
93+
### Droplet (Manual)
94+
95+
SSH into an Ubuntu 24.04 Droplet and run:
96+
97+
```bash
98+
curl -O https://raw.githubusercontent.com/pRizz/opencode-cloud/main/docker-compose.yml
99+
apt-get update -y && apt-get install -y docker.io && systemctl enable --now docker
100+
docker compose up -d
101+
```
102+
103+
Retrieve the IOTP and open `http://localhost:3000` via SSH tunnel:
104+
105+
```bash
106+
docker compose logs | grep -F "INITIAL ONE-TIME PASSWORD (IOTP): " | tail -n1 | sed 's/.*INITIAL ONE-TIME PASSWORD (IOTP): //'
107+
```
108+
109+
Docs: `docs/deploy/digitalocean-droplet.md`
94110

95111
## Features
96112

docs/deploy/digitalocean-droplet.md

Lines changed: 77 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -225,13 +225,80 @@ occ restart
225225

226226
Use this if you don't want `occ` installed on the Droplet.
227227

228-
> **Tip:** You can also use `docker compose up -d` with the project's
229-
> [`docker-compose.yml`](https://raw.githubusercontent.com/pRizz/opencode-cloud/main/docker-compose.yml)
230-
> instead of manual `docker run` commands. The Docker image also declares
231-
> `VOLUME` directives for all critical paths, providing anonymous volume
232-
> fallback if you forget explicit `-v` flags.
228+
> The Docker image declares `VOLUME` directives for all critical paths,
229+
> providing anonymous volume fallback if you forget explicit `-v` flags.
230+
> Named volumes (used by both methods below) are recommended for durable
231+
> persistence.
233232
234-
### 1) Install Docker
233+
### Path 2A: Docker Compose (Recommended)
234+
235+
Docker Compose configures all 6 named volumes automatically.
236+
237+
#### 1) Install Docker
238+
239+
```bash
240+
apt-get update -y
241+
apt-get install -y docker.io curl jq
242+
systemctl enable --now docker
243+
```
244+
245+
#### 2) Download docker-compose.yml
246+
247+
```bash
248+
curl -O https://raw.githubusercontent.com/pRizz/opencode-cloud/main/docker-compose.yml
249+
```
250+
251+
#### 3) Start the service
252+
253+
```bash
254+
docker compose up -d
255+
```
256+
257+
#### 4) Retrieve the IOTP
258+
259+
```bash
260+
docker compose logs | grep -F "INITIAL ONE-TIME PASSWORD (IOTP): " | tail -n1 | sed 's/.*INITIAL ONE-TIME PASSWORD (IOTP): //'
261+
```
262+
263+
#### 5) Access via SSH tunnel (recommended default)
264+
265+
From your laptop:
266+
267+
```bash
268+
ssh -L 3000:localhost:3000 root@<droplet-ip>
269+
```
270+
271+
Then open `http://localhost:3000`.
272+
273+
Enter the IOTP on the login page first-time setup panel, then enroll a passkey
274+
for the default `opencoder` account or use username/password fallback.
275+
276+
#### 6) Expose publicly (optional)
277+
278+
Edit `docker-compose.yml` and change the port binding:
279+
280+
```yaml
281+
ports:
282+
- "3000:3000" # was "127.0.0.1:3000:3000"
283+
```
284+
285+
Then restart:
286+
287+
```bash
288+
docker compose down && docker compose up -d
289+
```
290+
291+
Firewall recommendations:
292+
293+
- Allow inbound `3000/tcp` **only** from your IP (or office/VPN CIDR).
294+
- Keep inbound `22/tcp` restricted.
295+
- Consider adding HTTPS via Caddy (see [Optional HTTPS](#optional-https-caddy) above).
296+
297+
### Path 2B: Docker CLI (Manual)
298+
299+
Use this if you prefer direct `docker run` commands without Compose.
300+
301+
#### 1) Install Docker
235302

236303
```bash
237304
apt-get update -y
@@ -240,7 +307,7 @@ systemctl enable --now docker
240307
docker --version
241308
```
242309

243-
### 2) Create Docker volumes
310+
#### 2) Create Docker volumes
244311

245312
```bash
246313
docker volume create opencode-data
@@ -251,7 +318,7 @@ docker volume create opencode-config
251318
docker volume create opencode-users
252319
```
253320

254-
### 3) Run the container (SSH tunnel default: bind host port to localhost)
321+
#### 3) Run the container (SSH tunnel default: bind host port to localhost)
255322

256323
```bash
257324
docker run -d --name opencode-cloud-sandbox \
@@ -271,7 +338,7 @@ Notes:
271338
- Prefer a **pinned tag** (like `15.2.0`) for reproducible deployments.
272339
- See Docker Hub for tags: https://hub.docker.com/r/prizz/opencode-cloud-sandbox
273340

274-
### 4) Access via SSH tunnel
341+
#### 4) Access via SSH tunnel
275342

276343
From your laptop:
277344

@@ -295,7 +362,7 @@ docker logs opencode-cloud-sandbox 2>&1 | grep -F "INITIAL ONE-TIME PASSWORD (IO
295362

296363
Use the login page first-time setup panel with that IOTP, then continue to passkey setup where you can either enroll a passkey for `opencoder` or choose username/password registration to create your first managed user.
297364

298-
### 5) Expose publicly (optional)
365+
#### 5) Expose publicly (optional)
299366

300367
- Change to `-p 0.0.0.0:3000:3000` (or `-p 3000:3000`)
301368
- Apply a DigitalOcean firewall allowlist (recommended)

packages/core/README.md

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,31 @@ docker compose logs | grep -F "INITIAL ONE-TIME PASSWORD (IOTP): " | tail -n1 |
8282

8383
Docs: `docs/deploy/docker-desktop.md`
8484

85-
## Deploy to DigitalOcean (Coming Soon)
85+
## Deploy to DigitalOcean
8686

87-
DigitalOcean Marketplace one-click deployment is not implemented yet. Support is coming soon.
87+
### Marketplace (Coming Soon)
8888

89-
Warning: direct manual Droplet deployments are currently not recommended because persistence support is incomplete and data loss is likely.
89+
DigitalOcean Marketplace one-click deployment is in progress. Support is coming soon.
9090

91-
For testing-only reference:
92-
- Manual Droplet setup: `docs/deploy/digitalocean-droplet.md`
93-
- Marketplace docs/status: `docs/deploy/digitalocean-marketplace.md`
91+
Docs: `docs/deploy/digitalocean-marketplace.md`
92+
93+
### Droplet (Manual)
94+
95+
SSH into an Ubuntu 24.04 Droplet and run:
96+
97+
```bash
98+
curl -O https://raw.githubusercontent.com/pRizz/opencode-cloud/main/docker-compose.yml
99+
apt-get update -y && apt-get install -y docker.io && systemctl enable --now docker
100+
docker compose up -d
101+
```
102+
103+
Retrieve the IOTP and open `http://localhost:3000` via SSH tunnel:
104+
105+
```bash
106+
docker compose logs | grep -F "INITIAL ONE-TIME PASSWORD (IOTP): " | tail -n1 | sed 's/.*INITIAL ONE-TIME PASSWORD (IOTP): //'
107+
```
108+
109+
Docs: `docs/deploy/digitalocean-droplet.md`
94110

95111
## Features
96112

packages/core/src/docker/files/entrypoint.sh

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,56 @@ EOF
253253
log "Created default auth config at ${config_jsonc}."
254254
}
255255

256+
check_auth_enabled() {
257+
local config_dir="/home/opencoder/.config/opencode"
258+
local config_file=""
259+
260+
for candidate in "${config_dir}/opencode.json" "${config_dir}/opencode.jsonc"; do
261+
if [ -f "${candidate}" ]; then
262+
config_file="${candidate}"
263+
break
264+
fi
265+
done
266+
267+
if [ -z "${config_file}" ]; then
268+
return 1
269+
fi
270+
271+
local auth_enabled
272+
# Strip // line-comments before parsing (JSONC compat)
273+
auth_enabled="$(grep -v '^\s*//' "${config_file}" | jq -r '.auth.enabled // false' 2>/dev/null || echo "false")"
274+
[ "${auth_enabled}" = "true" ]
275+
}
276+
277+
warn_security_posture() {
278+
if ! check_auth_enabled; then
279+
log "================================================================="
280+
log "SECURITY WARNING: Authentication is not enabled."
281+
log "Anyone who can reach this container can use opencode without signing in."
282+
log "Enable auth by creating or editing the opencode config:"
283+
log " /home/opencoder/.config/opencode/opencode.jsonc"
284+
log ' { "auth": { "enabled": true } }'
285+
log "Docs: https://github.com/pRizz/opencode-cloud/tree/main/docs/deploy"
286+
log "================================================================="
287+
fi
288+
289+
# Advisory HTTPS notice for cloud deployments binding to all interfaces
290+
if [ "${OPENCODE_HOST}" = "0.0.0.0" ] || [ "${OPENCODE_HOST}" = "::" ]; then
291+
if detect_droplet; then
292+
log "================================================================="
293+
log "SECURITY NOTICE: opencode is binding to all network interfaces."
294+
log "If this container is exposed to the internet without HTTPS,"
295+
log "credentials and session data will be transmitted in the clear."
296+
log ""
297+
log "Recommended: terminate TLS in front of opencode."
298+
log " - Reverse proxy (Caddy, Nginx, Traefik)"
299+
log " - Cloud load balancer with TLS"
300+
log "Caddy setup: https://github.com/pRizz/opencode-cloud/blob/main/docs/deploy/digitalocean-droplet.md#optional-https-caddy"
301+
log "================================================================="
302+
fi
303+
fi
304+
}
305+
256306
restore_users() {
257307
shopt -s nullglob
258308
local records=(/var/lib/opencode-users/*.json)
@@ -475,6 +525,7 @@ EOF
475525
restore_or_bootstrap_users
476526
migrate_unmanaged_home_users_to_records
477527
sync_bootstrap_state
528+
warn_security_posture
478529

479530
log "Starting opencode on ${OPENCODE_HOST}:${OPENCODE_PORT}"
480531
/usr/local/bin/opencode-broker &

scripts/should-run-docker-check.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ fi
2525
is_docker_risk_path() {
2626
case "$1" in
2727
.dockerignore) return 0 ;;
28+
docker-compose.yml) return 0 ;;
2829
packages/core/src/docker/*) return 0 ;;
2930
.github/workflows/ci.yml) return 0 ;;
3031
.github/workflows/docker-publish.yml) return 0 ;;

0 commit comments

Comments
 (0)