diff --git a/.claude/agents/command-builder.md b/.claude/agents/command-builder.md index f477839..36e326a 100644 --- a/.claude/agents/command-builder.md +++ b/.claude/agents/command-builder.md @@ -117,7 +117,7 @@ Yes if it has side effects, is invoked deliberately by the user, and runs end-to ls .claude/skills/ ``` -Pick 2-3 task-shaped exemplars (e.g., this project's `release`, `ci-status`, `cloudflared-tunnel`) and read their SKILL.md to match house style — section structure, command-quoting conventions, how reports are formatted. +Pick 2-3 task-shaped exemplars (e.g., this project's `release`, `ci-status`, `agent-browser`) and read their SKILL.md to match house style — section structure, command-quoting conventions, how reports are formatted. ### Phase 3 — Author SKILL.md diff --git a/.claude/skills/agent-browser/SKILL.md b/.claude/skills/agent-browser/SKILL.md index cc8459b..57cb72e 100644 --- a/.claude/skills/agent-browser/SKILL.md +++ b/.claude/skills/agent-browser/SKILL.md @@ -74,8 +74,8 @@ Rationale for the pins: - **0.8.5** — later majors have breaking CLI changes; the rest of this skill targets 0.8.5's flags. -Or rebuild the image with `INSTALL_BROWSER=true` in the compose overlay -to bake this in. +Or rebuild the image with `INSTALL_AGENT_BROWSER=true` in +`.devcontainer/.env` to bake this in. After installing, resume the health check from step 2b — **do not stop**. @@ -123,7 +123,7 @@ agent-browser close ### Step 2d — DNS resolution check (tunnel hostnames) -If the URL contains a hostname that routes through a cloudflared tunnel (any `.` served via cloudflared), the container's DNS may not resolve it. Check and fix: +If the URL contains a hostname served by a tunnel or reverse proxy, the container's DNS may not resolve it yet. Check and fix: ```bash HOSTNAME=$(echo "$URL" | sed 's|https\?://||; s|/.*||; s|:.*||') @@ -135,7 +135,7 @@ if ! getent hosts "$HOSTNAME" &>/dev/null; then echo "$IP $HOSTNAME" | sudo tee -a /etc/hosts >/dev/null echo "Added $HOSTNAME -> $IP to /etc/hosts (container DNS didn't resolve it)" else - echo "FAIL: $HOSTNAME does not resolve. Check cloudflared tunnel DNS routing." + echo "FAIL: $HOSTNAME does not resolve. Check tunnel/DNS routing." # Stop here fi fi @@ -177,7 +177,7 @@ FILENAME=$(echo "$URL" | sed 's|https\?://||; s|/|--|g; s|--$||; s|[^a-zA-Z0-9._ agent-browser screenshot ".claude/screenshots/${FILENAME}.png" ``` -Example: `https://my-app.oh-local.localhost:8443/guide/exposure/` → `.claude/screenshots/my-app.oh-local.localhost-8443--guide--exposure.png` +Example: `https://my-app.oh-local.localhost:8443/guide/configuration/` → `.claude/screenshots/my-app.oh-local.localhost-8443--guide--configuration.png` ### Step 5 — Report diff --git a/.claude/skills/harness-audit/SKILL.md b/.claude/skills/harness-audit/SKILL.md index d2bc16d..0dba6d7 100644 --- a/.claude/skills/harness-audit/SKILL.md +++ b/.claude/skills/harness-audit/SKILL.md @@ -176,8 +176,6 @@ Launch 4 Agent tool calls **in a single message**. Each receives the Context Sna > > 4. **State corruption risks** — Look for: shared files written by multiple agents concurrently (e.g., `MEMORY.md`), no file locking on append operations, mid-commit crash scenarios (partial writes to critical files), compose volumes that could diverge. > -> 5. **Tunnel fragility** — Check `.claude/skills/cloudflared-tunnel/SKILL.md` and any compose services named `tunnel` or `cloudflared`. Is there auto-restart? Health monitoring? What happens if the tunnel drops silently? -> > **Return format (Ultra compression):** > ``` > CRITIC_FINDINGS @@ -282,7 +280,7 @@ See `context/rules/memory.md` for the canonical Memory Improvement Protocol. |---------|--------------| | PM | Onboarding, skill consistency, issue templates, wiki/memory utilization | | Implementer | Startup reliability, test coverage, CI/CD, package health, compose overlays | -| Critic | Security, heartbeat reliability, worktree cleanup, state corruption, tunnel | +| Critic | Security, heartbeat reliability, worktree cleanup, state corruption | | Explorer | Memory quality, wiki utilization, heartbeat health, worktree status, skill usage | ### Severity and effort definitions diff --git a/.claude/skills/strategic-proposal/SKILL.md b/.claude/skills/strategic-proposal/SKILL.md index ca068d8..075db14 100644 --- a/.claude/skills/strategic-proposal/SKILL.md +++ b/.claude/skills/strategic-proposal/SKILL.md @@ -82,7 +82,7 @@ Assemble a structured markdown briefing to pass to ALL 5 experts: - API routes: none ### Infrastructure -- Docker Compose + PostgreSQL 16 + cloudflared tunnel +- Docker Compose + opt-in PostgreSQL 16 overlay - CI/CD: GitHub Actions (lint, format, type-check, build, test, E2E) - Release: CalVer → GHCR Docker image - Agent: 8 skills, 7 sub-agents, 4 heartbeats, memory protocol diff --git a/.devcontainer/docker-compose.postgres.yml b/.devcontainer/docker-compose.postgres.yml new file mode 100644 index 0000000..c6be848 --- /dev/null +++ b/.devcontainer/docker-compose.postgres.yml @@ -0,0 +1,49 @@ +# PostgreSQL Service Overlay +# =========================== +# Purpose: Run PostgreSQL 16 as a sibling service for sandbox database operations. +# Default credentials: user=sandbox, password=sandbox, database=sandbox +# Network: Sandbox and postgres share isolated network (devnet) for inter-service communication +# Storage: pgdata volume persists database across container restarts +# Health: Includes healthcheck; sandbox waits for postgres to be ready before starting +# Environment vars injected into sandbox: DATABASE_URL, PGHOST, PGUSER, PGPASSWORD, PGDATABASE +# Usage: docker compose -f docker-compose.yml -f docker-compose.postgres.yml up +# Connection: psql postgresql://sandbox:sandbox@postgres:5432/sandbox (from inside sandbox) + +services: + postgres: + image: postgres:16-alpine + container_name: ${SANDBOX_NAME:-openharness}-postgres + environment: + POSTGRES_USER: sandbox + POSTGRES_PASSWORD: sandbox + POSTGRES_DB: sandbox + volumes: + - pgdata:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U sandbox"] + interval: 5s + timeout: 5s + retries: 5 + networks: + - devnet + + sandbox: + depends_on: + postgres: + condition: service_healthy + environment: + - DATABASE_URL=postgresql://sandbox:sandbox@postgres:5432/sandbox + - PGHOST=postgres + - PGUSER=sandbox + - PGPASSWORD=sandbox + - PGDATABASE=sandbox + networks: + - devnet + +networks: + devnet: + name: ${SANDBOX_NAME:-openharness}-devnet + driver: bridge + +volumes: + pgdata: diff --git a/AGENTS.md b/AGENTS.md index 3ccdb9e..33f9d96 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -107,7 +107,6 @@ Remove the sandbox. |-------|------| | `/release` | CalVer release — branch, tag, push, GHCR | | `/ci-status` | After `git push` — poll CI, report pass/fail | -| `/cloudflared-tunnel` | One-time named tunnel setup (remote mode) | | `/agent-browser` | Open a URL headless for screenshots / preview checks | | `/prd` | Generate a new PRD from a feature description | | `/ralph` | Convert markdown PRD → `tasks//prd.json` for the Ralph runner | @@ -122,10 +121,10 @@ the `Lifecycle` section above. There is no dedicated skill. ## Exposing apps -There is no first-class exposure tool right now. For external access, -either enable the `cloudflared` compose overlay (see -`docs/integrations/cloudflare.md`) or stand up your own reverse proxy -in front of the sandbox. +There is no first-class exposure tool. For external access, stand up +your own reverse proxy (nginx/Caddy/Traefik) or tunnel (cloudflared, +ngrok, tailscale-funnel) in front of the sandbox — the base ships +without any of these. Long-running apps inside the sandbox go in named tmux sessions, related apps as stacked panes — see `context/rules/sandbox-processes.md`. diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c7e1c9..fa44da8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Update policy and release automation live in [`.claude/rules/git.md`](.claude/ru ## [Unreleased] ### Removed +- In-tree docker-compose overlays under `.devcontainer/` except `docker-compose.postgres.yml`: the five host-auth overlays (`claude-host.yml`, `codex-host.yml`, `opencode-host.yml`, `pi-host.yml`, `deepagents-host.yml`), the three SSH overlays (`ssh.yml`, `ssh-generate.yml`, `sshd.yml`), `git.yml`, and `cloudflared.yml`. The host-auth overlays existed to share host agent credentials when the host UID matched the sandbox's UID 1000; `.devcontainer/entrypoint.sh` now syncs the sandbox user's UID/GID to the bind-mounted repo's owner on boot (replacing the host-group-membership workaround), so files written in either direction stay readable without per-agent overlays. Agent auth still persists in named volumes (`claude-auth`, `codex-auth`, `pi-auth`, `opencode-auth`, `deepagents-auth`, `cloudflared-auth`, `gh-config`); `gh auth login` covers git over HTTPS without per-overlay setup. Postgres remains opt-in via `docker-compose.postgres.yml`. Removed companion infrastructure: `install/cloudflared-tunnel.sh`, the `/cloudflared-tunnel` skill, the `*_HOST_BIND_MOUNT` / `SANDBOX_PASSWORD` / `INSTALL_BROWSER` / `INSTALL_CLOUDFLARED` / `GIT_COMMON_DIR` / `HOST_SSH_DIR` / `HOST_*_DIR` env plumbing in `.devcontainer/entrypoint.sh`, and the host-UID overlay-stripping block in `scripts/install.sh`. Deleted doc: `docs/integrations/cloudflare.md`. The `composeOverrides[]` mechanism in `config.json` is retained so downstream packs (Pi extensions, BYO harness packs) can still register their own overlays. ([#283](https://github.com/ryaneggz/open-harness/issues/283)) - Orphaned `.pi/onboard-steps/slack.ts` (and the empty `.pi/onboard-steps/` directory). The wizard logic lives in `apps/oh/src/config/slack.ts` now, with the bugs from the original mifune port fixed (correct tmux session name `agent-pi`, real `pi` launch path, token-prefix validation, allowlist-ID validation, atomic env-file merge). ([#281](https://github.com/ryaneggz/open-harness/issues/281)) - Stale documentation pages: `docs/onboarding.md`, `docs/sandbox-lifecycle.md`, `docs/connecting.md`, `docs/operations/{provision,destroy,repair}.md`, and `docs/architecture/{overview,container-runtime,crons-and-heartbeats}.md`. These pages described flows and tooling that no longer match the harness; rather than rewrite them, the docs site is trimmed to `intro`, `quickstart`, `installation`, and the `guide` / `agents` / `integrations` sections as the smaller accurate foundation. `context/rules/repo-layout-source.md` is also removed — it pinned `docs/architecture/container-runtime.md#repo-layout` as the canonical repo-tree source; with that file gone, the rule has no anchor. Cross-references in `AGENTS.md` (which is `CLAUDE.md` via symlink), `README.md`, `docs/intro.md`, `tasks/README.md`, `apps/README.md`, `crons/README.md`, `.claude/skills/harness-context/SKILL.md`, `context/rules/directory-readme.md`, `context/rules/memory.md`, `context/README.md`, `context/rules/README.md`, `.devcontainer/.example.env`, and `apps/docs/src/pages/index.tsx` updated to point at surviving docs or removed where they no longer apply. The brief `## Project Structure` block in `AGENTS.md` plus per-directory `README.md` files now stand in for the deleted repo-layout tree. ([#279](https://github.com/ryaneggz/open-harness/issues/279)) - `.pi/overlays/docker-compose.slack.yml` and all references to it. The in-tree Pi extension at `.pi/extensions/slack/` reads `SLACK_APP_TOKEN` / `SLACK_BOT_TOKEN` directly from `process.env` at `session_start`, so the compose-time wiring overlay is redundant. `docs/integrations/slack.md`, `README.md`, `docs/architecture/container-runtime.md`, `docs/guide/bring-your-own-harness.md`, `.devcontainer/docker-compose.yml`, and `.devcontainer/.example.env` no longer reference the overlay; `.pi/overlays/` directory removed. Source of truth for Slack env wiring is `.pi/extensions/slack/README.md`. diff --git a/Makefile b/Makefile index 1b638c9..eb67601 100644 --- a/Makefile +++ b/Makefile @@ -5,26 +5,19 @@ SANDBOX_NAME ?= openharness COMPOSE_BASE := -f .devcontainer/docker-compose.yml -COMPOSE_OVERRIDES := $(shell jq -r '.composeOverrides[]?' \ - config.json 2>/dev/null | sed 's|^|-f |' | tr '\n' ' ') +# Base ships with no required overlays. Downstream packs (Pi extensions, +# BYO harness packs) register their own by appending paths to +# composeOverrides[] in config.json; jq is only invoked when both jq and +# config.json are present. +COMPOSE_OVERRIDES := $(shell command -v jq >/dev/null 2>&1 && [ -f config.json ] && \ + jq -r '.composeOverrides[]?' config.json 2>/dev/null | sed 's|^|-f |' | tr '\n' ' ') COMPOSE := docker compose $(COMPOSE_BASE) $(COMPOSE_OVERRIDES) .DEFAULT_GOAL := help -.PHONY: sandbox shell destroy stop logs ps restart help _check-jq _check-config +.PHONY: sandbox shell destroy stop logs ps restart help -_check-jq: - @command -v jq >/dev/null 2>&1 || { \ - echo "ERROR: jq is required to expand config.json composeOverrides."; \ - echo "Install jq (e.g. apt install jq, brew install jq) and re-run."; \ - exit 1; } - -_check-config: - @[ -f config.json ] || { \ - echo "ERROR: config.json missing. Run scripts/install.sh, OR: cp config.example.json config.json"; \ - exit 1; } - -sandbox: _check-jq _check-config ## Provision and start the sandbox +sandbox: ## Provision and start the sandbox $(COMPOSE) up -d --build shell: ## Connect to the sandbox (agent choice happens inside) diff --git a/README.md b/README.md index 8770971..6a0ec4b 100644 --- a/README.md +++ b/README.md @@ -42,8 +42,8 @@ Prefer VS Code or remote SSH? Use the Dev Containers extension's "Attach to Runn `.devcontainer/.env` is generated with safe defaults during install. Open it any time to change `SANDBOX_NAME`, set a different `GH_TOKEN`, change -`TZ`, set `SANDBOX_PASSWORD` (sshd overlay), or set `SLACK_*` tokens for -the Pi Slack extension. Apply with `make destroy && make sandbox`. +`TZ`, or set `SLACK_*` tokens for the Pi Slack extension. Apply with +`make destroy && make sandbox`.
Manual setup (no installer) diff --git a/apps/docs/src/pages/index.tsx b/apps/docs/src/pages/index.tsx index b9fbaa6..3dd69f4 100644 --- a/apps/docs/src/pages/index.tsx +++ b/apps/docs/src/pages/index.tsx @@ -82,7 +82,7 @@ const WHY: Array<{ title: string; body: string }> = [ }, { title: "Composable substrate", - body: "Postgres, Cloudflare tunnels, SSH, the Caddy gateway — cherry-pick via Compose overlays. Multi-agent setups ship as separate harness packs.", + body: "Postgres ships as an opt-in compose overlay; add tunnels, reverse proxies, or multi-agent setups as harness packs.", }, ]; @@ -185,7 +185,7 @@ export default function Home(): React.ReactElement { Open Harness runs as a single long-lived Docker container dedicated to your project. The bind-mounted workspace/ houses whatever your project needs — one repo, several, side-by-side branches, scratch dirs. The agent owns its workspace; your laptop stays clean.

- A markdown cron runtime reads crons/*.md and wakes the agent on a schedule — issue triage, PR review, background grooming, anything you want running while you sleep. Configure the sandbox via .devcontainer/.env; add infra (Postgres, Cloudflare tunnels, SSH, Caddy gateway) via Compose overlays. + A markdown cron runtime reads crons/*.md and wakes the agent on a schedule — issue triage, PR review, background grooming, anything you want running while you sleep. Configure the sandbox via .devcontainer/.env; Postgres ships as an opt-in compose overlay, and additional infra (tunnels, reverse proxies) is registered via harness-pack overlays in config.json.

Multi-agent setups — like a Pi+Mom Slack bot — ship as separate harness packs you git clone into the workspace. @@ -220,7 +220,7 @@ export default function Home(): React.ReactElement { Documentation - Quickstart, architecture, overlays + Quickstart, architecture, agents diff --git a/context/rules/git.md b/context/rules/git.md index fe559d3..7567c5e 100644 --- a/context/rules/git.md +++ b/context/rules/git.md @@ -34,13 +34,6 @@ Detect via `git show-ref --verify --quiet refs/heads/` (or remote `refs/re Inside sandbox, run `gh auth login && gh auth setup-git` during onboarding. GitHub CLI installs credential helper — `git push` / `git fetch` use your GitHub token — no SSH keys required. -Advanced opt-ins (only when git-over-SSH explicitly needed): - -- `docker-compose.ssh.yml` — mounts host `~/.ssh` read-only (reuses existing keys) -- `docker-compose.ssh-generate.yml` — generates persistent ED25519 keypair in named volume (add to GitHub manually) - -Mutually exclusive — pick at most one. Both off by default. - ## PR Titles Format: `FROM TO ` (literal) diff --git a/docs/agents/deepagents.md b/docs/agents/deepagents.md index 5cc5f92..e7f4c04 100644 --- a/docs/agents/deepagents.md +++ b/docs/agents/deepagents.md @@ -61,15 +61,6 @@ on PATH and **configured** only when `~/.deepagents/.env` or `~/.deepagents/config.toml` is non-empty — an empty mounted directory is not treated as authenticated. -### Reusing host DeepAgents state - -If you already use DeepAgents on the host, opt in to the -`deepagents-host` overlay to bind-mount your host `~/.deepagents` into the -sandbox. The overlay requires host UID 1000 and a pre-existing host -`~/.deepagents` directory; DeepAgents stores raw provider API keys with no -scope or expiry, so enable it only when you've accepted that blast radius -explicitly. - ## State persistence and repo-local `.deepagents/` Two separate directories carry DeepAgents state, with very different @@ -77,7 +68,7 @@ durability and review semantics: | Path | Scope | Persistence | Notes | |---|---|---|---| -| `~/.deepagents/` | Per-sandbox user state | Survives rebuilds via the `deepagents-auth` volume (or host bind-mount) | Provider keys, model defaults, memory, skills, sessions live here. **Only place secrets here.** | +| `~/.deepagents/` | Per-sandbox user state | Survives rebuilds via the `deepagents-auth` volume | Provider keys, model defaults, memory, skills, sessions live here. **Only place secrets here.** | | `/.deepagents/` | Per-project | Whatever git decides | Project memory and skills the agent may load from the workspace root. **Treat as project data — follows normal `.gitignore` and code-review rules.** | A repo-local `.deepagents/` directory may be read by the agent and may be @@ -156,8 +147,7 @@ Two environment overrides apply: > via `RALPH_DEEPAGENTS_FLAGS` grants unrestricted non-interactive shell > execution. Combined with the mounted Docker socket (enabled by default > in the base compose file), this can affect sibling containers or the -> host Docker daemon, and combined with the `deepagents-host` overlay can -> reach raw host provider keys. Only use `all` for trusted tasks where +> host Docker daemon. Only use `all` for trusted tasks where > you have accepted that risk explicitly. ## Tips diff --git a/docs/agents/opencode.md b/docs/agents/opencode.md index 4f96a5c..8b297d4 100644 --- a/docs/agents/opencode.md +++ b/docs/agents/opencode.md @@ -50,10 +50,6 @@ tmux new-session -d -s agent-opencode 'opencode' tmux attach -t agent-opencode ``` -## Host auth overlay - -The `opencode-host` overlay bind-mounts your host OpenCode state from `~/.local/share/opencode` into the sandbox. Enable it only for trusted workflows, because any code running in the sandbox can read those credentials. - ## Upstream documentation - [OpenCode CLI](https://opencode.ai/docs/cli/) diff --git a/docs/installation.md b/docs/installation.md index 4688fcd..ecaa34d 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -26,7 +26,7 @@ The installer: 1. Verifies Docker and git are present. 2. Clones the repo into `~/.openharness` (or pulls latest if the directory already exists). -3. Prompts for `SANDBOX_NAME` and `SANDBOX_PASSWORD`, then writes `.devcontainer/.env`. +3. Prompts for `SANDBOX_NAME`, then writes `.devcontainer/.env`. 4. Runs `docker compose -f .devcontainer/docker-compose.yml up -d --build`. 5. Prints the next-step commands (open a shell, stop, tear down). @@ -37,9 +37,8 @@ The installer: | `OH_INSTALL_REF=` | Pin the cloned repo to a specific tag or SHA instead of `main`. | | `OH_ASSUME_YES=1` | Accept defaults at every prompt. | | `SANDBOX_NAME=` | Skip the "Container name" prompt. | -| `SANDBOX_PASSWORD=` | Skip the credential prompt (used by the optional `sshd` overlay). | -`SANDBOX_NAME` and `SANDBOX_PASSWORD` resolve independently — set one and the other still prompts. Both fall back to defaults (`openharness` and `changeme`) when no TTY is available. +`SANDBOX_NAME` falls back to the default (`openharness`) when no TTY is available. ## Manual installation @@ -112,9 +111,8 @@ Debian Bookworm (slim). The `sandbox` user has passwordless sudo. | Tool | Purpose | |------|---------| -| Docker CLI + Compose | Container management from inside the sandbox (with docker overlay) | +| Docker CLI + Compose | Container management from inside the sandbox (host docker socket bind-mounted by the base compose) | | GitHub CLI (`gh`) | PRs, issues, releases from the terminal | -| cloudflared | Cloudflare Tunnel for public URLs to dev servers | | tmux | Detachable terminal sessions for long-running agents | | croner | Markdown-frontmatter cron scheduler for autonomous agent tasks | @@ -127,7 +125,7 @@ Debian Bookworm (slim). The `sandbox` user has passwordless sudo. | ripgrep (`rg`) | Fast code search | | curl, wget | HTTP clients | | nano | Text editor | -| openssh-server | SSH server (enabled via sshd overlay) | +| openssh-client | `ssh-keygen` for GitHub auth flows | | bash-completion | Tab completion | ### Shell aliases @@ -143,10 +141,12 @@ codex → codex --dangerously-bypass-approvals-and-sandbox Auth credentials survive container rebuilds via named Docker volumes: -- `claude-auth` → `~/.claude` (Claude Code OAuth) — or, with the `claude-host` overlay, a RW bind-mount of your host `~/.claude`. -- `codex-auth` → `~/.codex` (Codex OAuth) — or, with the `codex-host` overlay, a RW bind-mount of your host `~/.codex`. -- `opencode-auth` → `~/.local/share/opencode` (OpenCode OAuth; `auth.json`) — or, with the `opencode-host` overlay, a RW bind-mount of your host `~/.local/share/opencode`. -- `pi-auth` → `~/.pi` (Pi Agent OAuth) — or, with the `pi-host` overlay, a RW bind-mount of your host `~/.pi`. -- `deepagents-auth` → `~/.deepagents` (DeepAgents provider keys, memory, skills, sessions) — or, with the `deepagents-host` overlay, a RW bind-mount of your host `~/.deepagents`. Repo-local `.deepagents/` is **project data** and follows normal `.gitignore` and code-review rules — never put secrets there. -- `cloudflared-auth` → `~/.cloudflared` (Cloudflare credentials) +- `claude-auth` → `~/.claude` (Claude Code OAuth) +- `codex-auth` → `~/.codex` (Codex OAuth) +- `opencode-auth` → `~/.local/share/opencode` (OpenCode OAuth; `auth.json`) +- `pi-auth` → `~/.pi` (Pi Agent OAuth) +- `deepagents-auth` → `~/.deepagents` (DeepAgents provider keys, memory, skills, sessions). Repo-local `.deepagents/` is **project data** and follows normal `.gitignore` and code-review rules — never put secrets there. +- `cloudflared-auth` → `~/.cloudflared` (Cloudflare tunnel credentials, when used) - `gh-config` → `~/.config/gh` (GitHub CLI tokens) + +Downstream harness packs and Pi extensions can introduce additional volumes or bind-mount overlays by adding their own compose file to `composeOverrides[]` in `config.json`. diff --git a/docs/integrations/cloudflare.md b/docs/integrations/cloudflare.md deleted file mode 100644 index 48fc44d..0000000 --- a/docs/integrations/cloudflare.md +++ /dev/null @@ -1,72 +0,0 @@ ---- -sidebar_position: 3 -title: "Cloudflare" ---- - -# Cloudflare - -Open Harness exposes sandbox apps to the public internet via **Cloudflare named tunnels**. Use the `/cloudflared-tunnel` skill (or run `install/cloudflared-tunnel.sh` directly) to create a tunnel, write its ingress config, and route DNS — all in one idempotent step. - -## Enabling the cloudflared overlay - -To install `cloudflared` inside the sandbox, add the cloudflared overlay at startup: - -```bash -docker compose \ - -f .devcontainer/docker-compose.yml \ - -f .devcontainer/docker-compose.cloudflared.yml \ - up -d --build -``` - -This sets `INSTALL_CLOUDFLARED=true` in the container environment. The entrypoint installs the `cloudflared` binary to `/usr/local/bin/cloudflared` during container startup. - -## Named tunnels via cloudflared-tunnel.sh - -For persistent public URLs tied to a Cloudflare-managed domain, use `install/cloudflared-tunnel.sh`. This script creates a named tunnel, writes the ingress config, and routes DNS — all idempotently. The `/cloudflared-tunnel` skill drives this same script with the canonical onboarding flow (prereq checks, multi-ingress YAML, DNS routing, optional `--run`, post-start verification). - -### Prerequisites - -1. `cloudflared` installed (provided by the cloudflared overlay above). -2. Authenticated with Cloudflare: `cloudflared login` (opens browser, saves `~/.cloudflared/cert.pem`). -3. A domain managed in your Cloudflare account. - -### Usage - -Single ingress: - -```bash -install/cloudflared-tunnel.sh -``` - -Multiple ingress routes: - -```bash -install/cloudflared-tunnel.sh : [: ...] -``` - -Start the tunnel immediately after configuration: - -```bash -install/cloudflared-tunnel.sh myproject app.example.com:3000 api.example.com:3001 --run -``` - -Replace `example.com` with your own Cloudflare-managed domain. - -### Running the tunnel in tmux - -For a long-lived tunnel, run it in a named tmux session: - -```bash -tmux new-session -d -s expose-public-3000 \ - 'cloudflared tunnel run myproject 2>&1 | tee /tmp/expose-public-3000.log' -``` - -Attach to inspect logs: - -```bash -tmux attach -t expose-public-3000 -``` - -### Driving the flow with the skill - -Prefer the `/cloudflared-tunnel` skill when working interactively — it parses arguments, checks prerequisites, creates or reuses the tunnel, writes `~/.cloudflared/config-.yml`, routes DNS for every hostname, and (with `--run`) starts the tunnel and verifies each hostname end-to-end. See `.claude/skills/cloudflared-tunnel/SKILL.md` for the canonical step-by-step. diff --git a/docs/integrations/github.md b/docs/integrations/github.md index 84db26e..2536058 100644 --- a/docs/integrations/github.md +++ b/docs/integrations/github.md @@ -40,26 +40,6 @@ gh run list --branch feat/my-feature gh issue view 42 ``` -## Optional SSH overlays - -For cases where Git-over-SSH is required (custom Git servers, deploy keys, or workflows that do not support HTTPS), two compose overlays are available: - -| Overlay | Behavior | -|---|---| -| `docker-compose.ssh.yml` | Mounts your host `~/.ssh` directory into the sandbox read-only. Uses your existing SSH keys without copying them. | -| `docker-compose.ssh-generate.yml` | Generates a new persistent ED25519 keypair in a named Docker volume. The public key must be added to GitHub manually before use. | - -These overlays are mutually exclusive — use at most one. Both are off by default. Add one when starting the sandbox: - -```bash -docker compose \ - -f .devcontainer/docker-compose.yml \ - -f .devcontainer/docker-compose.ssh.yml \ - up -d --build -``` - -For most users, `gh auth login` and `gh auth setup-git` are sufficient and no SSH overlay is needed. - ## Persisting credentials across restarts -The `gh` token is stored inside the container at `~/.config/gh/`. Because this path is inside the container, it is lost when the container is removed. To persist credentials across `docker compose down -v` cycles, either re-run `gh auth login` after each rebuild, or bind-mount a host directory to `~/.config/gh/` via a custom compose overlay. +The `gh` token is stored inside the container at `~/.config/gh/`, backed by the named `gh-config` volume. The token survives `docker compose down` and `docker compose up` cycles. `docker compose down -v` removes the volume — re-run `gh auth login` after a `down -v`, or register your own compose overlay in `config.json` `composeOverrides[]` to bind-mount a host directory to `~/.config/gh/`. diff --git a/docs/quickstart.md b/docs/quickstart.md index 63fefbd..e845374 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -70,7 +70,10 @@ safe defaults during install. The most-edited values: Apply changes with `make destroy && make sandbox`. -For Postgres, Cloudflare tunnels, SSH, etc., chain compose overlays from `.devcontainer/`. +For additional services, the base ships with an opt-in Postgres overlay +at `.devcontainer/docker-compose.postgres.yml`; for anything else +(tunnels, reverse proxies), register your own compose overlay by +appending it to `composeOverrides[]` in `config.json`. ## Next steps @@ -79,7 +82,6 @@ With the sandbox running, here's what to wire up next: - Slack bridge for your sandbox agent → [integrations/slack.md](./integrations/slack.md) - GitHub auth (only if you skipped `GH_TOKEN` at install) → [integrations/github.md](./integrations/github.md) - Postgres via Compose overlay → [integrations/postgres.md](./integrations/postgres.md) -- Cloudflare tunnels for external access → [integrations/cloudflare.md](./integrations/cloudflare.md) ## Tear down