Skip to content
This repository was archived by the owner on Jun 7, 2026. It is now read-only.
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
13 changes: 6 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
## Scope

- This repo owns the static-web wrapper only. `opencode/` is an upstream git submodule; do not edit files under it here. The only allowed upstream change is moving the submodule pointer.
- This repo owns the static-web wrapper only. `opencode/` is an upstream git submodule; do not edit files under it here except to move the submodule pointer.
- Wrapper-owned code lives in `build/`, `runtime/`, `config/`, `scripts/`, `tests/`, and root Docker/CI/package config.
- If you must inspect upstream code, read the closest `AGENTS.md` inside `opencode/` first; upstream's default branch is `dev`, not `main`.

## Runtime Wiring

- `Dockerfile` is the real integration path: it installs the filtered upstream app workspace, patches `opencode/packages/app/src/entry.tsx` with `build/patch-upstream-app-source.ts`, builds `opencode/packages/app`, copies wrapper sources, runs `build/check-runtime-config-compat.ts`, bundles `runtime/index.ts`, prepares `opencode/packages/app/dist`, then serves it with nginx.
- `Dockerfile` is the integration source of truth: it installs only the filtered upstream app workspace, patches `opencode/packages/app/src/entry.tsx`, builds `opencode/packages/app`, runs `build/check-runtime-config-compat.ts`, bundles `runtime/index.ts`, prepares `opencode/packages/app/dist`, then serves it with nginx.
- `runtime/generate-nginx-config.sh` is the runtime generation source of truth. It runs as `/docker-entrypoint.d/40-opencode-web.sh` under Alpine `/bin/sh`, requires contiguous unpadded indexes starting at `1`, normalizes hostnames/backend URLs, writes per-host configs under `/opt/opencode-web/runtime-configs/<host>.js`, and regenerates nginx config from `config/nginx.conf.template`.
- `runtime/runtime-config-core.ts` is the browser bootstrap: it replaces the persisted server list and default server URL with the injected backend for that host, prunes other servers/credentials, and removes legacy `server.v3`.
- `build/prepare-static-web.ts` injects `/runtime-config.js` and writes `opencode-web-customizations.css` from `build/customization-css.ts`; source-level URL injection belongs in `build/patch-upstream-app-source.ts` before the upstream app build.
- `config/nginx.conf.template` is the base nginx cache/CSP contract: unmatched hosts return 404 except `/health`, configured hosts are appended by the generator, only `/assets/` is immutable, and all other configured-host responses stay `no-store` with `add_header ... always`.
- `config/nginx.conf.template` plus the generator define the nginx cache/CSP/routing contract: unmatched hosts return 404 except `/health`, configured host routes fall back to `/index.html`, only `/assets/` is immutable, and all other configured-host responses stay `no-store` with `add_header ... always`.

## Compatibility Contracts

- Root `bun test` runs only `./tests` because root `bunfig.toml` sets `[test].root = "./tests"`.
- Root `bun test` runs only `./tests` because root `bunfig.toml` sets `[test].root = "./tests"`; use `bun test tests/<name>.test.ts` for a focused root test.
- `tests/*.contracts.ts` encode every wrapper assumption about upstream app internals, runtime persistence, CSS selectors, and nginx CSP/cache behavior. If upstream changes break the wrapper, update the contract and wrapper code together.
- `bun run test:compat` is the same upstream-compat guard used during `docker build`; keep it free of extra root-only dependencies because Docker invokes `bun ./build/check-runtime-config-compat.ts` without installing the root package.
- `bun run test:compat` is the upstream-compat guard used during `docker build`; keep it free of root-only dependencies because Docker invokes `bun ./build/check-runtime-config-compat.ts` without installing the root package.
- CSP/cache headers are intentionally duplicated in `config/nginx.conf.template` and `runtime/generate-nginx-config.sh`; keep them in sync.

## Commands
Expand All @@ -29,7 +29,6 @@
- Upstream compatibility check only: `bun run test:compat`
- Runtime bundle only: `bun run build:runtime`
- Static web preparation only, after the upstream app dist exists: `bun run build:prepare-static -- opencode/packages/app/dist`
- Focused root tests: `bun test tests/<name>.test.ts`, for example `bun test tests/compatibility-contracts.test.ts`
- Runtime/image regression check: `bun run test:runtime-config -- --build`; without `--build`, the script expects an existing image tag, defaulting to `opencode-web-docker`.
- End-to-end build: `bun run docker:build` or `docker build -t opencode-web-docker .`
- Docker-equivalent upstream app build: `bun install --cwd opencode --filter @opencode-ai/app --frozen-lockfile --ignore-scripts` then `bun ./build/patch-upstream-app-source.ts ./opencode/packages/app/src` then `OPENCODE_CHANNEL=prod bun run --cwd opencode/packages/app build -- --sourcemap false`
Expand All @@ -46,4 +45,4 @@
- Shell scripts are `/bin/sh`/Alpine-compatible; keep `runtime/generate-nginx-config.sh` env scanning newline-safe so multiline values cannot create fake `SERVER_<N>_*` names.
- The final image runs as non-root `nginx` on port `8080`; keep `/opt/opencode-web/public` read-only and generated config/writable state under `/etc/nginx/conf.d`, `/opt/opencode-web/runtime-configs`, or `/var/cache/nginx`.
- Root TypeScript uses `erasableSyntaxOnly`; avoid enums, namespaces, parameter properties, or other TS syntax that needs transpilation in wrapper `.ts` files.
- Bun version sources are duplicated: `package.json` `packageManager` drives GitHub `setup-bun`, while `Dockerfile` and `@types/bun` must be reviewed separately when changing Bun.
- Bun version sources are duplicated: `package.json` pins `packageManager` and `@types/bun`, `Dockerfile` pins `oven/bun`, and workflows call `oven-sh/setup-bun` without an explicit version.
8 changes: 4 additions & 4 deletions runtime/generate-nginx-config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,12 @@ encode_base64() {
printf '%s' "$1" | base64 | tr -d '\n'
}

runtime_root="/opt/opencode-web"
runtime_root="${OPENCODE_WEB_RUNTIME_ROOT:-/opt/opencode-web}"
public_root="$runtime_root/public"
runtime_config_root="$runtime_root/runtime-configs"
runtime_bundle_path="$runtime_root/runtime/runtime-bundle.js"
nginx_template_path="$runtime_root/config/nginx.conf.template"
nginx_config_path="/etc/nginx/conf.d/default.conf"
nginx_config_path="${OPENCODE_WEB_NGINX_CONFIG_PATH:-/etc/nginx/conf.d/default.conf}"
nginx_servers_marker="# OPENCODE_WEB_GENERATED_SERVERS"
nginx_listen_port="8080"

Expand Down Expand Up @@ -220,7 +220,7 @@ server {
listen $nginx_listen_port;
listen [::]:$nginx_listen_port;
server_name $host;
root /opt/opencode-web/public;
root $public_root;
index index.html;
$(write_no_store_headers)

Expand All @@ -230,7 +230,7 @@ $(write_no_store_headers)
}

location = /runtime-config.js {
alias /opt/opencode-web/runtime-configs/$host.js;
alias $runtime_config_root/$host.js;
}

location ^~ /assets/ {
Expand Down
Loading