From 522c29878cbf17996a388ff0967cdfb4f3cf5f10 Mon Sep 17 00:00:00 2001 From: Ales Rechtorik Date: Thu, 7 May 2026 14:06:26 +0200 Subject: [PATCH 1/2] docs(zcp): add Zerops Control Plane docs section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reconciles the pv-zcp positioning manifesto and kh-zcp ops manual into a single ZCP docs set. Adds: - /features/coding-agents.mdx — features-page positioning anchor with ZCP MCP vs hosted-service framing, comparison vs Lovable/E2B/Replit/ Codespaces/Cloudflare, capability-by-job table. - /features/local-remote-development.mdx — paired three-modes story (Local+VPN / Cloud IDE on ZCP / Native IDE over SSH). - /zcp/ — 17-page reference subsection: overview, quickstart, glossary, how-it-works, choose-workspace + hosted/local setup, trust-model + tokens + production-policy + auditing, create-or-adopt + build-and- verify + delivery-handoff + package-running, agent-workflow contract reference, troubleshooting. - Mermaid diagrams: where-ZCP-runs architecture, bootstrap-routes decision tree, develop-flow loop, trust-model blast radii. - CustomCard pairs and DocCardList for entry-point pages. Sidebar: new "Zerops Control Plane" subsection under References, between Command Line Tools and Networking. "Infrastructure for Coding Agents" added at the bottom of the Features section. Removes the orphan /guides/local-development.mdx (superseded by /zcp/setup/local-agent-bridge and /features/local-remote-development). Co-Authored-By: Claude Opus 4.7 (1M context) --- .gitignore | 6 + apps/docs/content/features/coding-agents.mdx | 141 ++++++++++++ .../features/local-remote-development.mdx | 141 ++++++++++++ .../docs/content/guides/local-development.mdx | 131 ----------- .../docs/content/zcp/concept/how-it-works.mdx | 105 +++++++++ apps/docs/content/zcp/glossary.mdx | 66 ++++++ apps/docs/content/zcp/overview.mdx | 34 +++ apps/docs/content/zcp/quickstart.mdx | 84 +++++++ .../content/zcp/reference/agent-workflow.mdx | 215 ++++++++++++++++++ .../content/zcp/reference/troubleshooting.mdx | 210 +++++++++++++++++ .../zcp/security/auditing-agent-work.mdx | 157 +++++++++++++ .../zcp/security/production-policy.mdx | 70 ++++++ .../security/tokens-and-project-access.mdx | 147 ++++++++++++ .../docs/content/zcp/security/trust-model.mdx | 97 ++++++++ .../content/zcp/setup/choose-workspace.mdx | 102 +++++++++ .../content/zcp/setup/hosted-workspace.mdx | 95 ++++++++ .../content/zcp/setup/local-agent-bridge.mdx | 123 ++++++++++ .../zcp/workflows/build-and-verify-app.mdx | 101 ++++++++ .../workflows/create-or-adopt-services.mdx | 80 +++++++ .../zcp/workflows/delivery-handoff.mdx | 85 +++++++ .../zcp/workflows/package-running-service.mdx | 141 ++++++++++++ apps/docs/docusaurus.config.js | 3 + apps/docs/sidebars.js | 160 +++++++++++++ apps/docs/src/css/custom.css | 20 ++ 24 files changed, 2383 insertions(+), 131 deletions(-) create mode 100644 apps/docs/content/features/coding-agents.mdx create mode 100644 apps/docs/content/features/local-remote-development.mdx delete mode 100644 apps/docs/content/guides/local-development.mdx create mode 100644 apps/docs/content/zcp/concept/how-it-works.mdx create mode 100644 apps/docs/content/zcp/glossary.mdx create mode 100644 apps/docs/content/zcp/overview.mdx create mode 100644 apps/docs/content/zcp/quickstart.mdx create mode 100644 apps/docs/content/zcp/reference/agent-workflow.mdx create mode 100644 apps/docs/content/zcp/reference/troubleshooting.mdx create mode 100644 apps/docs/content/zcp/security/auditing-agent-work.mdx create mode 100644 apps/docs/content/zcp/security/production-policy.mdx create mode 100644 apps/docs/content/zcp/security/tokens-and-project-access.mdx create mode 100644 apps/docs/content/zcp/security/trust-model.mdx create mode 100644 apps/docs/content/zcp/setup/choose-workspace.mdx create mode 100644 apps/docs/content/zcp/setup/hosted-workspace.mdx create mode 100644 apps/docs/content/zcp/setup/local-agent-bridge.mdx create mode 100644 apps/docs/content/zcp/workflows/build-and-verify-app.mdx create mode 100644 apps/docs/content/zcp/workflows/create-or-adopt-services.mdx create mode 100644 apps/docs/content/zcp/workflows/delivery-handoff.mdx create mode 100644 apps/docs/content/zcp/workflows/package-running-service.mdx diff --git a/.gitignore b/.gitignore index 656e2e62..2eb42ac1 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,9 @@ www/**/.yarn/* build/** **/dist **/stats + +# ZCP local-mode runtime state — written by `zcp init` / `zcp serve` +.zcp/ + +# Generated full-text knowledge artifact (built via `zerops-llm-script.ts` prebuild) +knowledge/ diff --git a/apps/docs/content/features/coding-agents.mdx b/apps/docs/content/features/coding-agents.mdx new file mode 100644 index 00000000..c8bfc10d --- /dev/null +++ b/apps/docs/content/features/coding-agents.mdx @@ -0,0 +1,141 @@ +--- +title: Infrastructure for Coding Agents +description: ZCP MCP gives a coding agent project-scoped operations on a real Zerops project — discover, deploy, verify, operate, recover, hand off. Same control plane whether it runs hosted in the project or as a binary on your laptop. +--- + +import CustomCard from '/src/components/CustomCard'; + +Coding agents need somewhere to operate, not just somewhere to generate code. The hard part is rarely "write the function" — it's where the agent builds, deploys, tests, and iterates against real infrastructure across hours and sessions, with state that persists and a path to production. + +**Zerops Control Plane (ZCP)** is an MCP server — the `zcp` binary — that gives a coding agent project-scoped operations on a real Zerops project. Discover services, deploy through the standard pipeline, verify against the real URL, read logs, classify failures, hand off. The deploy is real, the URL is real, the verify hits the route a user would. You hold the intent and the quality bar; ZCP holds the truth about the platform. + +> The LLM does the engineering. The developer holds intent and judgment. ZCP holds reality. + +:::caution Public preview — keep production in a separate project +ZCP carries a project-scoped token with full rights so the agent can operate the services you select. It belongs in a development or staging project, not a production one. See [Production boundary](/zcp/security/production-policy). +::: + +## Who it's for + +Developers using a coding agent on real application work — provisioning services, editing code, deploying through the Zerops pipeline, verifying against real URLs, recovering from failure, reporting back. ZCP doesn't replace the developer; it's the bridge between the developer's intent and the platform that runs the result. + +Today ZCP supports **Claude Code**, paired with a **Claude Pro or Max subscription**. Other MCP-aware agents will follow as their integrations stabilize. + +## What ZCP MCP does + +The ZCP MCP exposes a fixed set of operations on one Zerops project, grouped by user job. The agent never has to be told what the project looks like — ZCP reports it from what's deployed and running right now. + +| Job | What it means | Reference | +|---|---|---| +| **Discover** | Read services, env vars, recent activity from live platform state | [How ZCP works](/zcp/concept/how-it-works) | +| **Deploy** | Ship code through the standard build and deploy pipeline | [Build and verify an app](/zcp/workflows/build-and-verify-app) | +| **Verify** | Reachability + behavior checks against the actual URL | [Build and verify an app](/zcp/workflows/build-and-verify-app#verify) | +| **Operate** | Lifecycle, scaling, env vars, public access | [How ZCP works](/zcp/concept/how-it-works) | +| **Recover** | Failure classification, ZCP status, resume across interruptions | [Troubleshooting](/zcp/reference/troubleshooting) | +| **Hand off** | Decide how finished work ships — direct, git, or external CI | [Choose how finished work ships](/zcp/workflows/delivery-handoff) | + +A session runs in three phases: **Bootstrap** (read what's already in the project, pick a route), **Develop** (edit, deploy, verify, iterate against classified failures), **Deliver** (decide how future changes ship). A green deploy with a 500 on the relevant route is not done — verification has two layers and both must pass. + +Full contract — bootstrap routes, mode semantics, failure categories, recovery: [Agent workflow reference](/zcp/reference/agent-workflow). + +## Two ways to run it + +ZCP is a binary; the question is where it runs. + +
+ + A Zerops service (`zcp@1`) that runs the ZCP MCP inside the project, with optional Cloud IDE and bundled Claude Code. Safe by design — no path to your laptop or other projects. **Recommended starting point.** + + + The `zcp` binary on your laptop, connected to the project VPN. The agent runs in your editor. Real and supported, but the most WIP path — expect setup churn between releases. + +
+ +Either way, the agent gets the same project-scoped operations against the same project. The hosted service packages ZCP with an editor and an agent CLI; the local bridge is the same MCP without those extras. + +Decision: [Choose your workspace](/zcp/setup/choose-workspace). Broader local-dev story (humans, not just agents): [Local & Remote Development](/features/local-remote-development). + +## What the hosted workspace adds + +When you pick the hosted path, ZCP MCP ships as part of a `zcp@1` Zerops service that bundles two optional components on top of the MCP: + +**Coding Agent.** An agentic CLI — currently Claude Code — preinstalled and pre-authenticated with MCP wiring. Authenticate either with OAuth into your Anthropic subscription, or with an API token in the container's secret env. + +**Cloud IDE.** Browser-based VS Code (code-server) with the Claude Code plugin, SSHFS access to runtime services in the project, and a curated dev toolchain. Reachable VPN-only or on a `.zerops.app` subdomain gated by an auto-generated password. Useful when you want to watch the agent work and step in occasionally rather than running it autonomously over SSH. Full toolchain inventory: [Local & Remote Development → Browser Cloud IDE on ZCP](/features/local-remote-development#browser-cloud-ide-on-zcp). + +Without either toggle, the hosted ZCP service is an MCP endpoint plus a Linux dev container with `zcli`, the platform-injected token, and project-private network reach — useful as a shared remote workstation. With both, it's a one-click agent + IDE workspace inside the project. + +## Why transparent infrastructure works for agents + +Agents reason over text. The choices Zerops makes for human transparency are also what make the platform legible to agents: + +- `zerops.yaml` is structured and documented. The agent reads, modifies, and commits it the way it would any other config file. +- Resource allocation is queryable. The agent can compute what a change costs before scaling. +- Service topology is explicit — services have names, ports, and dependencies the agent can reason about directly. +- Logs, events, and deploy history are streamed. The agent verifies outcomes instead of guessing. +- Dev, staging, and production share infrastructure. The agent's deploy to staging exercises the same pipeline production uses. + +The MCP server is the ergonomic layer on top. It tightens the loop and gives the host agent a permission surface to gate against — but it doesn't expose anything `zcli` and SSH don't already reach. Without MCP, an agent on a Zerops project would still beat one in a sandbox; it would just be slower to find the right tool. + +## MCP and permissions + +ZCP MCP groups its operations into three categories so a host agent can gate them by policy: + +
+ + Inspect state and configuration. Safe to auto-allow. Example: `zerops_logs`, `zerops_events`, `zerops_discover`. + + + Mutate the project or its services. Require confirmation. Example: `zerops_deploy`, `zerops_scale`, `zerops_delete`. + + + Set up infrastructure or coordinate work. Gated by team policy. Example: `zerops_recipe`, `zerops_mount`, `zerops_subdomain`. + +
+ +That's the surface. The boundary is the project. ZCP's token authorizes one project, full stop — cross-project, account-wide, or organization actions stay outside reach. Project roles (Owner, Admin, Developer, Guest) apply to ZCP itself; see [Roles & Permissions](/features/rbac). + +Full tool list and permission semantics: [agent workflow reference](/zcp/reference/agent-workflow). Audit surface — deploys, events, logs, git history — is the same any human session leaves: [Auditing agent work](/zcp/security/auditing-agent-work). Token shape and confirmation gates: [Tokens and credentials](/zcp/security/tokens-and-project-access). + +## Hacking on the hosted workspace + +The hosted workspace is a normal Zerops service running the `zcp@1` Ubuntu base image. The configuration generated when you toggle Coding Agent and Cloud IDE is a starting point, not a sealed product — install packages, run additional processes alongside the agent, fork the base image and ship a hardened version with team-standard tools baked in. A developer-onboarding script worth a few hundred lines can live in the workspace's init configuration and apply to every workspace and every agent session automatically. Patterns: [Hack on the workspace](/zcp/setup/hosted-workspace#hack-on-the-workspace). + +The local agent bridge isn't extensible the same way; the binary on your laptop is just the MCP — editor, shell, and dev server stay your tools' job. + +## Source control + +When the agent runs in the hosted workspace, it lives in a container, not on your laptop — git access wires through `gh auth login` (the GitHub CLI is preinstalled), through a fine-grained token stored in ZCP's secret env vars, or through SSH agent forwarding from your local editor. When the agent runs in the local bridge, it uses your existing git credentials. Detail: [Tokens and credentials → Git credentials](/zcp/security/tokens-and-project-access#git-credentials--separate-from-zcp_api_key). + +## How this differs from related tools + +The agent space contains several products that look superficially similar but solve different problems. + +**One-shot generators (Lovable, Bolt, v0).** Prompt-to-prototype tools. You describe an app, get an artifact you can preview and maybe export. The output exists in isolation — no real database, no production path, no persistent state between sessions. ZCP is the opposite end: the output is a running system on production-grade infrastructure from the first deploy. + +**Code-execution sandboxes (E2B and similar).** Designed for agents to run code safely in isolation. The sandbox spins up, the agent executes, the sandbox tears down. No managed database, no service network, no deploy pipeline — by design. ZCP solves the next problem: where does an agent build, deploy, and iterate against real infrastructure across hours and sessions. + +**AI-enabled hosted IDEs (Replit Agent and similar).** Closer to ZCP than the generators above. The structural differences are environment fidelity and ecosystem openness. Replit's runtimes and managed services approximate production rather than match it; the IDE, deployment, and hosting are all proprietary. ZCP uses standard Linux, standard Postgres, standard SSH, standard runtimes — nothing requires you to stay inside ZCP. Local + VPN connects from any local editor. + +**Agent primitive collections (Cloudflare-style).** A set of APIs the agent orchestrates across — versioned storage, model gateways, vector search. ZCP gives the agent a place to live where everything is on one network, accessible by hostname, no API orchestration required. Different bet on what scales: breadth of primitives versus depth of environment. + +**AI-enabled CDEs (Codespaces + Copilot, Gitpod + AI).** Adding AI to a code editor isn't the same as giving the agent infrastructure to operate. The agent in those environments can write code; it can't provision a Postgres, deploy a service, or read production logs. + +| | Persistent workspace | Real services | Deploy pipeline | Env parity | Standard tooling | +|---|:-:|:-:|:-:|:-:|:-:| +| One-shot generators | – | – | – | – | – | +| Replit | ✓ | ~ | ~ | – | – | +| E2B | – | – | – | – | ✓ | +| Codespaces / Gitpod | ✓ | – | – | – | ✓ | +| Cloudflare-style | – | ~ | ~ | – | – | +| **ZCP** | **✓** | **✓** | **✓** | **✓** | **✓** | + +## Where to start + + diff --git a/apps/docs/content/features/local-remote-development.mdx b/apps/docs/content/features/local-remote-development.mdx new file mode 100644 index 00000000..b143dfa1 --- /dev/null +++ b/apps/docs/content/features/local-remote-development.mdx @@ -0,0 +1,141 @@ +--- +title: Local & Remote Development +description: Three ways to develop on Zerops — local with VPN, the browser Cloud IDE on ZCP, or your native IDE over SSH. Same network, same managed services, same pipeline as production. +--- + +You can develop on Zerops without leaving your laptop, fully inside Zerops, or anywhere in between. The project's private network is the thing you join — what you use to join it is a choice, not a constraint. Same hostnames, same credentials, same managed services, and the same deploy pipeline whether you're editing on `apidev` from your local IDE or running a hot-reload loop in a container in the cloud. + +This is also what closes the gap between "works on my machine" and production. The Postgres your dev container talks to and the Postgres production talks to are the same Zerops-managed service type at different resource levels, on the same kind of private network, configured by the same `zerops.yml`. Dev isn't an approximation of prod — it's prod with smaller numbers. + +:::note +ZCP (Zerops Control Plane) is a service you can add to any project. Two of the three modes below run inside it. The third — local development with `zcli vpn up` — doesn't require ZCP at all. +::: + +## How development on Zerops works + +Every project ships with a private network. Services inside it reach each other by hostname (`db:5432`, `api:3000`, `redis:6379`), with credentials injected as environment variables. Standard hostname resolution on a network that happens to be yours. + +The development question is just: how do you, the developer, get onto that network? Three answers, all equivalent in capability. + +- **Local + VPN.** Your laptop joins the private network via WireGuard. Editor, toolchain, and processes stay where they are. ZCP is not required. +- **Browser Cloud IDE on ZCP.** A Linux container in the project, accessed through VS Code Server in the dashboard. Zero local setup. +- **Native IDE over SSH.** SSH from your local VS Code, JetBrains Gateway, Cursor, or plain `ssh` — to ZCP, or directly to a service container. + +Same `db:5432`, same `api:3000`, same credentials in all three. Switch modes without changing anything about the project. + +## Local + VPN + +**Best for:** keeping your existing editor, dotfiles, and toolchain — and offloading just the dependencies that are painful to run locally. + +```bash +zcli vpn up + +psql "postgresql://app:secret@db:5432/myapp" +redis-cli -h cache -p 6379 +ssh apidev +``` + +What you get on your laptop: every service in the project addressable by hostname, passwordless SSH into any container, real Postgres with prod-shaped data, real Redis, real S3-compatible storage. No mocks. No Docker Compose drift between what you run locally and what production runs. + +What you don't have to do: install ZCP, run `zcli push`, learn a new editor, change your dev loop. If you're used to running `npm run dev` against a local Postgres, run it against `db:5432` instead — same connection string format, same SQL, same managed instance type as the one production will use. + +This mode is also where coding agents running on your laptop fit. A local Claude Code or Cursor session inherits the VPN's access, so the agent can hit `db:5432` and SSH into containers the same way you can. It can consume managed services, but it can't operate the project — see [Infrastructure for Coding Agents](/features/coding-agents) for that. + +**Source control.** Use git on your laptop the way you always have. Commits, pushes, SSH agent against GitHub, your existing credentials — nothing about Zerops sits between you and the remote. + +:::info Complete VPN setup +See the [VPN reference guide](/references/networking/vpn). +::: + +## Browser Cloud IDE on ZCP + +**Best for:** zero local setup, working from a machine that isn't yours, or wanting a pristine environment that doesn't touch your laptop. + +When you add ZCP to a project, you get an Ubuntu container running the `zcp@1` base image with a curated dev toolchain — `git`, `gh`, `jq`, `yq`, `ripgrep`, `fd`, `fzf`, `bat`, `tree`, `tmux`, `htop`, `ncdu`, `httpie`, `make`, `psql`, `mysql`, `redis-cli`, `chrome` and `puppeteer`, `sshfs`, `zsh` with Oh My Zsh, plus `zcli` and the Zerops MCP server. Optionally, browser-based VS Code (the Cloud IDE) with a Claude Code plugin and an agentic CLI bundled in. + +ZCP also mounts your dev services over SSHFS, so you can edit code that runs in another container as if it were local: + +```bash +ls /var/www/apidev +ls /var/www/frontenddev +vim /var/www/apidev/src/server.ts +``` + +The dev server in the mounted container hot-reloads, the database is the same `db:5432` you'd use in any other mode, and `zcli push --service apistage` deploys to staging through the production pipeline. One ZCP can mount multiple dev services at once, so a single workspace can cover the whole stack. + +The Cloud IDE itself is configurable. Two access methods: + +- **VPN Only.** The IDE is reachable through `zcli vpn up`, no public exposure. Most secure. +- **Public Access.** The IDE is reachable on a `.zerops.app` subdomain, gated by an auto-generated password. + +**Source control.** ZCP isn't on your laptop, so your local SSH keys don't follow it. Two patterns work: + +- **GitHub via `gh`.** The GitHub CLI is preinstalled. Run `gh auth login` once and `gh` handles HTTPS auth, PR creation, and review flows from the container. +- **Token in env vars.** Save a fine-grained personal access token to ZCP's secret environment variables; `git` reads it through a credential helper. + +Either pattern keeps your token in ZCP, not in your project's source. + +## Native IDE over SSH + +**Best for:** native editor UX without carrying the toolchain or databases on your laptop. + +Once `zcli vpn up` is connected, every container on the project's private network is reachable from your laptop over SSH. Any editor that speaks Remote SSH speaks to it: VS Code Remote SSH, JetBrains Gateway, Cursor, Zed, plain `vim`. + +```bash +ssh apidev # any service in the project +ssh frontenddev +ssh zcp # if you provisioned ZCP +``` + +In this mode, ZCP is convenient but not required. You can connect VS Code Remote SSH straight to `apidev`, edit the source there, run the dev server in the same container, and never provision ZCP at all. The project's private network does the rest. ZCP becomes a *convenience* layer: a single workspace that carries the toolchain, mounts multiple dev services over SSHFS, and has `zcli` and the MCP server preconfigured for the project. If you'd rather connect your editor directly to your runtime services and skip ZCP entirely, that works. + +**Source control.** SSH agent forwarding works the same way it does to any other machine. Add your local SSH key to your agent (`ssh-add`), and `git push` from inside `apidev` (or ZCP) uses your laptop's GitHub key over the forwarded agent socket — no token storage required. + +```ssh-config +# Local ~/.ssh/config +Host apidev.zerops + HostName apidev + ForwardAgent yes +``` + +:::info SSH access details +See the [SSH reference guide](/references/networking/ssh). +::: + +## Picking a mode + +| | Local + VPN | Cloud IDE on ZCP | Native IDE over SSH | +|---|---|---|---| +| Editor runs | Local | Browser | Local | +| Toolchain | Local | ZCP | ZCP or service container | +| Dev databases | Hostname via VPN | Native, on private network | Native, on private network | +| ZCP service required | No | Yes | No (convenience) | +| Git auth | Local credentials | `gh` or token in env | SSH agent forwarding | +| Best for | Keeping your setup | Disposable environments | Native UX, lighter laptop | + +You're not locked into a mode. The same project supports all three, and switching is free. A teammate on Local + VPN and an agent inside ZCP are hitting the same `db:5432`. + +## Same setup, dev to production + +The point worth pausing on: the development environments above don't approximate production — they share infrastructure with it. Same managed Postgres, same private network, same load balancer, same `zerops.yml`. A dev project, a stage project, and a production project differ in resource allocation and access rules, not in architecture. + +This is what makes the "but it works on my machine" failure mode hard to reach on Zerops. Your dev container's Postgres is the same managed service type production uses. Your queue is the same NATS or Kafka. Your storage is the same S3-compatible service. When code works in dev, you've already proven it works against the kind of infrastructure it'll meet in production. The remaining question is scale, not shape. + +This applies whether the developer is human or an agent — see [Infrastructure for Coding Agents](/features/coding-agents) for the agent case. + +## How this differs from cloud IDEs + +If you've used GitHub Codespaces or Gitpod, ZCP looks similar — a Linux container in the cloud, accessed from a browser or local IDE. Two structural differences are worth understanding before you compare them. + +**You don't have to be in the editor.** Codespaces and Gitpod are editor-first. Their value proposition assumes you're inside their environment; the database connections, dependencies, and toolchain all live there. Zerops is network-first. The Cloud IDE is one of three ways to reach the project's private network — `zcli vpn up` lets you skip the editor environment entirely and consume managed services from your existing local setup. There's no equivalent on Codespaces or Gitpod, because their architecture doesn't expose the network. + +**Dev, staging, and production are the same infrastructure.** On a CDE, your dev environment is one platform and your deployment target is another — different runtimes, different databases, different networking. On Zerops, they're the same platform. Same managed Postgres, same private network, same `zerops.yml`. Differences between dev and production are resource allocation, not architecture. The "works on my machine" gap doesn't open because the machines aren't different in kind. + +ZCP is also just a service. It's a container running the `zcp@1` Ubuntu base image — you can SSH into it, install your own packages, run your own processes, and configure it however you want. Nothing about it is a walled garden. + +## Next Steps + +- VPN setup and troubleshooting → [VPN Reference Guide](/references/networking/vpn) +- SSH access to services → [SSH Reference Guide](/references/networking/ssh) +- Build and deploy from any of these environments → [Prepare, Build, Deploy Pipeline](/features/pipeline) +- Coding agents on Zerops → [Infrastructure Platform for Coding Agents](/features/coding-agents) \ No newline at end of file diff --git a/apps/docs/content/guides/local-development.mdx b/apps/docs/content/guides/local-development.mdx deleted file mode 100644 index d0f8d029..00000000 --- a/apps/docs/content/guides/local-development.mdx +++ /dev/null @@ -1,131 +0,0 @@ ---- -title: "Local Development with Zerops" -description: "Develop locally with hot reload while connecting to Zerops managed services (DB, cache, storage) via VPN. ZCP generates `.env` with real credentials. Deploy to Zerops with `zerops_deploy` which uses `zcli push` under the hood." ---- - -Develop locally with hot reload while connecting to Zerops managed services (DB, cache, storage) via VPN. ZCP generates `.env` with real credentials. Deploy to Zerops with `zerops_deploy` which uses `zcli push` under the hood. - ---- - -## Setup - -### Prerequisites -- **zcli** installed: `npm i -g @zerops/zcli` or [docs.zerops.io/references/cli](https://docs.zerops.io/references/cli) -- **VPN**: WireGuard (installed by zcli automatically on first `zcli vpn up`) -- **Project-scoped token**: Create in Zerops GUI → Settings → Access Tokens → Custom access per project - -### Configuration -```json -// .mcp.json (in project root) -{ - "mcpServers": { - "zcp": { - "command": "zcp", - "env": { "ZCP_API_KEY": "" } - } - } -} -``` - ---- - -## Workflow - -### 1. Connect to Zerops services -```bash -zcli vpn up -``` -- All services accessible by hostname (e.g., `db`, `cache`) -- One project at a time — switching disconnects the current -- **Env vars NOT available via VPN** — use `.env` file instead - -### 2. Load credentials -ZCP generates `.env` from `zerops_discover`: -``` -db_host=db -db_port=5432 -db_password= -db_connectionString=postgresql://db:@db:5432/db -``` - -How to load: -| Runtime | Method | -|---------|--------| -| Node.js 20+ | `node --env-file .env app.js` | -| Next.js, Vite, Nuxt | Automatic (reads `.env`) | -| PHP/Laravel | Automatic (reads `.env`) | -| Python | `python-dotenv` or `django-environ` | -| Go | `godotenv.Load()` or `source .env && go run .` | -| Java/Spring | `spring-dotenv` or `application.properties` | - -### 3. Develop locally -Start your dev server as usual — hot reload works against Zerops managed services over VPN. - -### 4. Deploy to Zerops -``` -zerops_deploy targetService="appstage" -``` -Uses `zcli push` under the hood. Blocks until build completes. - ---- - -## zerops.yml for Local Mode - -The same `zerops.yml` works for both local push and container deploy: - -```yaml -zerops: - - setup: appstage - build: - base: nodejs@22 - buildCommands: - - npm ci - - npm run build - deployFiles: ./dist - run: - start: node dist/server.js - ports: - - port: 3000 - httpSupport: true - envVariables: - DB_URL: ${db_connectionString} -``` - -`${hostname_varName}` references are resolved by Zerops at container runtime — they work regardless of push source (local or container). - ---- - -## Connection Troubleshooting - -| Symptom | Diagnosis | Fix | -|---------|-----------|-----| -| `nc -zv db 5432` times out | VPN not connected | `zcli vpn up ` | -| VPN connected, still timeout | Wrong project | `zcli vpn up ` | -| Connected but auth fails | Stale .env | Regenerate from `zerops_discover includeEnvs=true` | -| Service unreachable | Service stopped | `zerops_manage action="start" serviceHostname="db"` | - -### Diagnostic sequence -1. `zerops_discover service="db"` — is service RUNNING? -2. `nc -zv db 5432 -w 3` — network reachable? -3. Compare `.env` vs `zerops_discover includeEnvs=true` — credentials current? - ---- - -## Multi-Project - -Each project directory has its own `.mcp.json` + `.zcp/state/`. VPN is one per machine — switch manually: -```bash -zcli vpn up # work on project A -zcli vpn up # auto-disconnects A, connects B -``` - ---- - -## Gotchas - -1. **VPN = network only**: Env vars must come from `.env` file, not VPN connection -2. **`.env` contains secrets**: Add to `.gitignore` immediately — never commit -3. **Deploy = new container**: Local files on Zerops are lost on every deploy. Only `deployFiles` content persists -4. **One VPN project at a time**: Connecting to project B disconnects project A -5. **Object storage (S3)**: Uses HTTPS apiUrl — may work without VPN but not fully verified. Include VPN as fallback -6. **zcli must be installed**: `zerops_deploy` requires zcli in PATH. Error message includes install link if missing diff --git a/apps/docs/content/zcp/concept/how-it-works.mdx b/apps/docs/content/zcp/concept/how-it-works.mdx new file mode 100644 index 00000000..ca738b4b --- /dev/null +++ b/apps/docs/content/zcp/concept/how-it-works.mdx @@ -0,0 +1,105 @@ +--- +title: "How ZCP works" +description: "ZCP MCP runs near the project, reads live platform state, and exposes a fixed set of project-scoped operations grouped by user job." +--- + +Zerops Control Plane (ZCP) is an MCP server — the `zcp` binary — that exposes a fixed set of project-scoped operations to a coding agent. It runs near the project, reads live platform state, and groups operations by user job. The agent never has to be told what the project looks like; ZCP reports it from what's deployed and running right now. + +## Where ZCP runs + +ZCP joins the project's private network — either as part of a hosted Zerops service inside the project, or as a binary on your laptop while you're connected to the project VPN. + +```mermaid +flowchart TB + subgraph proj["Zerops project (private network)"] + direction LR + runtime["Runtime services
appdev / appstage"] + managed["Managed services
db / cache / storage"] + end + + subgraph hosted["Hosted workspace — zcp@1 service inside the project"] + direction LR + mcpH["ZCP MCP"] + ideH["Cloud IDE
optional"] + agentH["Claude Code
optional"] + end + + subgraph laptop["Local agent bridge — your laptop"] + direction LR + binL["zcp binary
(ZCP MCP)"] + editorL["Your editor
+ Claude Code"] + end + + hosted -->|on the project network| proj + laptop -. via Zerops VPN .-> proj +``` + +- **Hosted workspace.** A Zerops service of type `zcp@1` runs the ZCP MCP inside the project, with optional bundled extras: a coding-agent CLI (Claude Code) and a browser-based VS Code (the Cloud IDE) with SSHFS access to runtime services and a curated dev toolchain. Created when you pick the **AI Agent** environment from a [Zerops recipe](https://app.zerops.io/recipes), or check **Add Zerops Control Plane (ZCP) service** during project creation. +- **Local agent bridge.** Run `zcp init` in your project directory and the `zcp` binary runs on your laptop. The agent runs in your editor; ZCP talks to the Zerops API on your behalf, project services are reached over your Zerops VPN. No editor, no SSHFS, no bundled agent — just the MCP. + +The MCP operations are the same in both modes. What differs is what's bundled around the MCP: the hosted service adds editor, agent, SSHFS mounts, server-side batch deploys, and a dev-server runner; the local bridge adds deploys from your working directory and `.env` generation for local apps. Decision: [Choose your workspace](/zcp/setup/choose-workspace). + +## Reads live platform state + +When the agent needs context, ZCP queries the platform — what services exist, what state they're in, what ports and env variables they expose. Build and deploy timelines come from the events surface, which the agent reads when diagnosing a failure or polling a git-push deploy. No long prompt, no stale documentation; the answer comes from the project as it stands. + +This is what makes three things work: + +- **Cold start.** ZCP discovers what's already provisioned instead of asking you to describe it. +- **Failure diagnosis.** ZCP reads the build logs, runtime logs, and event timeline directly, classifies the failure, and proposes the next step. +- **Session resume.** After an interruption, the agent asks ZCP for status and resumes from real state, not from chat memory. + +## What ZCP MCP lets the agent do + +Operations are grouped by user job. You ask for an outcome in plain language; the agent picks the right operations and reports what changed. + +### Discover + +Read the project before changing anything: which services exist, what state they're in, what ports and env variables they expose. When you ask "what is in this project?", you don't describe it — the agent asks ZCP and tells you. + +### Deploy + +Ship code through the standard Zerops [build and deploy pipeline](/guides/deployment-lifecycle). For direct deploys the call blocks until the build finishes and the runtime reports active — the agent waits with you, not for you. For git-push delivery, ZCP returns after the push lands and the agent polls events until the runtime is active and records the deploy. + +When several services ship together, the hosted workspace coordinates server-side batch deploys. The local bridge runs them serially from your working directory. + +### Verify + +Three post-deploy checks run automatically: service status, recent error logs, and an HTTP probe on the public URL for runtime services (managed services get status only). Those prove the service is reachable, not that the requested behavior works. + +If reachability passes, the agent layers behavior verification on top — opening the URL, hitting a specific endpoint, inspecting a database row — to confirm the change you asked for actually landed. Failures carry a structured classification with a category, likely cause, and next action; see [Troubleshooting → Failure categories](/zcp/reference/troubleshooting#start-from-a-failure-category). + +### Operate + +For service lifecycle — start, stop, restart, reload, [scaling](/features/scaling), [public access](/features/access) — the agent uses scoped operations that change one project at a time. [Environment variables](/features/env-variables) are read and set at service or project scope with preprocessor support, so the agent can generate strong secrets in place. + +When ZCP runs in the hosted workspace, the wrapping Zerops service also gives the agent SSHFS access to other services and the option to run a long-lived dev process inside another container. Those are workspace features, not MCP operations. + +### Recover + +Every failed deploy carries a structured failure classification: category, likely cause, suggested next action. The agent reads this before the raw logs. + +If the agent loses context — long session, browser crash, closed tab — it asks ZCP for status and resumes from current platform state. Recovery doesn't depend on the prior chat. + +### Hand off + +When the work is done, you and the agent decide how the next change ships: keep deploying directly, commit and push to git, or hand off to your CI. ZCP records the choice and configures any push credentials and build integrations needed. See [Choose how finished work ships](/zcp/workflows/delivery-handoff). + +For a different kind of handoff — turning a deployed service into reusable import files — see [Package a running service](/zcp/workflows/package-running-service). + +## Project boundary and confirmation gates + +Each operation knows the project boundary. ZCP can't reach a different project or push code your token doesn't authorize. In the local bridge, ZCP itself stays project-scoped, but the agent process inherits your user; what the client does on your laptop follows your client's permissions, not ZCP's. Full picture: [Trust model](/zcp/security/trust-model). + +Two operations carry an explicit confirmation gate because the loss isn't reversible from inside the conversation: + +- **Service deletion** — requires explicit user approval in the same conversation, by service name. +- **Wholesale service replacement** on a service with prior failed deploy history — first call is refused with a structured payload; second call must echo it back. + +Detail: [Tokens and credentials → Confirmation gates](/zcp/security/tokens-and-project-access#confirmation-gates-for-destructive-actions). + +## ZCP MCP and zCLI + +ZCP MCP and [zCLI](/references/cli) coexist. zCLI is what a human or CI runner uses (`zcli push`, `zcli vpn up`, scripts and Makefiles); ZCP MCP is what a coding agent uses. They share the same Zerops API, the same project, the same build pipeline. The local bridge expects zCLI to be installed for `zcli vpn up`; the hosted workspace ships with zCLI in the terminal next to the agent. + +Pick zCLI for commands you type and scripted automation. Pick ZCP MCP when an agent is driving and needs scoped operations, structured failure handling, post-deploy verify, and recovery from interrupted sessions. diff --git a/apps/docs/content/zcp/glossary.mdx b/apps/docs/content/zcp/glossary.mdx new file mode 100644 index 00000000..a4e747cf --- /dev/null +++ b/apps/docs/content/zcp/glossary.mdx @@ -0,0 +1,66 @@ +--- +title: "Glossary" +description: "Quick definitions for the terms used across ZCP docs — what ZCP is, where it runs, the contracts the agent follows, and the project shapes it picks up." +--- + +Quick definitions for the terms that appear across ZCP docs. The four sections below answer: what ZCP *is*, where ZCP *runs*, what the *agent* does, and what the *project* looks like. + +## What ZCP is + +**Zerops Control Plane (ZCP)** — the umbrella name. In context, can refer to the binary, the operations it exposes, or loosely to the wrapping Zerops service. When precision matters, use the more specific terms below. + +**ZCP MCP** — the project-scoped operations the `zcp` binary exposes to a coding agent over MCP: discover, deploy, verify, operate, recover, hand off. Same MCP surface whether ZCP runs hosted or local. + +**`zcp` binary** — the executable. Runs as a process inside the hosted service, or on your laptop in the local agent bridge. + +## Where ZCP runs + +**Hosted workspace** — a Zerops service of type `zcp@1` running inside your project. Bundles ZCP MCP plus optional Cloud IDE and a coding-agent CLI. The recommended starting point. Setup: [Provision a hosted workspace](/zcp/setup/hosted-workspace). + +**`zcp@1`** — the Zerops service type the hosted workspace runs as. The wrapping service, not the MCP itself. + +**`zcp` service** — an instance of `zcp@1` in a specific project. What you see in the Zerops dashboard. + +**Local agent bridge** — the `zcp` binary running on your laptop while you're connected to the project VPN. Same MCP operations, no editor or agent CLI bundled. The most WIP path. Setup: [Use ZCP locally](/zcp/setup/local-agent-bridge). + +**Cloud IDE** — the browser-based VS Code (code-server) the hosted workspace can serve. Optional toggle on the `zcp` service. + +**Coding Agent (toggle)** — bundles a coding-agent CLI (currently Claude Code) inside the hosted workspace, preconfigured with auth and MCP wiring. Optional toggle on the `zcp` service. + +## What the agent does + +**Bootstrap route** — how the agent starts a session: `adopt` existing services, start from a `recipe`, build a `classic` custom plan, or `resume` an interrupted session. Detail: [Agent workflow → Bootstrap routes](/zcp/reference/agent-workflow#bootstrap-routes). + +**Develop flow** — the fixed sequence the agent follows for a change: name scope → build → deploy → serve → verify reachability → verify behavior → fix and redeploy if needed. + +**Failure category** — the structured tag the platform attaches to a failed deploy: `build`, `start`, `verify`, `network`, `config`, `credential`, `other`. Drives the recovery path. Canonical home: [Agent workflow → Failure categories](/zcp/reference/agent-workflow#failure-categories). + +**Verification layers** — runtime reachability (a platform health check) and requested behavior (an application check the agent runs against the actual endpoint). Both must pass before a session reports complete. + +**Delivery contract** — what happens to *future* changes after a verified deploy: `auto` (direct deploy), `git-push` (commits go to a configured remote), `manual` (your CI takes over). Detail: [Choose how finished work ships](/zcp/workflows/delivery-handoff). + +**ZCP status** — a single MCP call that rebuilds the agent's view of the project from live platform state. The right recovery move when chat memory has drifted. + +## What the project looks like + +**Runtime scope** — the service the agent targets for a change (`appdev`, `appstage`, `app`). + +**Workspace shape** — ZCP's name for the project topology: + +- `standard` — dev runtime + explicit stage runtime (e.g. `appdev` + `appstage`) +- `dev` — one mutable dev runtime, no stage +- `simple` — one runtime, no dev/stage split +- `local-stage` — local checkout linked to a Zerops stage runtime +- `local-only` — local checkout, no Zerops runtime linked yet + +Decision context: [Choose your workspace → Workspace shapes](/zcp/setup/choose-workspace#workspace-shapes). + +**Service mode** — Zerops scaling shape (`HA`, `NON_HA`). Independent of workspace shape — see [Scaling](/features/scaling). + +**Recipe** — a Zerops-published starter project. With the **AI Agent** environment selected, recipes provision a `zcp@1` service alongside the runtime services. + +## Three names that get confused + +- **ZCP** (Zerops Control Plane) — what this section documents. +- **zCLI** — the [Zerops command-line client](/references/cli). What humans and CI use to push code, manage projects, connect over VPN. Coexists with ZCP; not part of it. +- **zsc** (Zerops Setup Control) — the [in-container utility](/references/zsc) that ships in every Zerops runtime and build container. Used by `zerops.yaml` build steps. Not part of ZCP. diff --git a/apps/docs/content/zcp/overview.mdx b/apps/docs/content/zcp/overview.mdx new file mode 100644 index 00000000..5801732e --- /dev/null +++ b/apps/docs/content/zcp/overview.mdx @@ -0,0 +1,34 @@ +--- +title: "Zerops Control Plane — section overview" +description: "What ZCP docs cover, where to start, and what stays in the platform's canonical references." +--- + +This section documents Zerops Control Plane (ZCP) for developers already using it. For the positioning — what ZCP is, why it exists, how it differs from sandboxes, hosted IDEs, and one-shot generators — start at [Infrastructure for Coding Agents](/features/coding-agents). + +ZCP is in public preview — use it for dev and staging projects, keep production in a separate Zerops project. The local agent bridge is the more WIP of the two paths; see [Choose your workspace](/zcp/setup/choose-workspace). + +## Where to start + +| If you want to… | Go to | +|---|---| +| Get a real app deployed in one session | [Quickstart](/zcp/quickstart) | +| Understand the mental model first | [How ZCP works](/zcp/concept/how-it-works) | +| Pick hosted vs local | [Choose your workspace](/zcp/setup/choose-workspace) | +| Set up the hosted workspace | [Provision a hosted workspace](/zcp/setup/hosted-workspace) | +| Set up ZCP on your laptop | [Use ZCP locally](/zcp/setup/local-agent-bridge) | +| Wire credentials and confirmation gates | [Tokens and credentials](/zcp/security/tokens-and-project-access) | +| Drive the deploy → verify loop | [Build and verify an app](/zcp/workflows/build-and-verify-app) | +| Decide how finished work ships | [Choose how finished work ships](/zcp/workflows/delivery-handoff) | +| Understand the trust boundary and blast radius | [Trust model](/zcp/security/trust-model) | +| Read the full agent contract | [Agent workflow reference](/zcp/reference/agent-workflow) | +| Diagnose a problem | [Troubleshooting](/zcp/reference/troubleshooting) | + +## What stays outside ZCP + +ZCP is a control plane, not the platform. The Zerops [build and deploy pipeline](/features/pipeline), [permissions](/features/rbac), [deployment lifecycle](/guides/deployment-lifecycle), networking, and scaling keep their own canonical documentation. ZCP docs explain how an agent uses those primitives; they don't replace the platform reference. + +## Three names that get confused + +- **ZCP** (Zerops Control Plane) — what this section documents. Exposes one Zerops project to a coding agent over MCP. +- **zCLI** — the [Zerops command-line client](/references/cli). What humans and CI use to push code, manage projects, connect over VPN. Not part of ZCP, but ZCP coexists with it. +- **zsc** (Zerops Setup Control) — the [in-container utility](/references/zsc) that ships in every Zerops runtime and build container. Used by `zerops.yaml` build steps to install runtime tech, scale resources, configure the container. Not part of ZCP either. diff --git a/apps/docs/content/zcp/quickstart.mdx b/apps/docs/content/zcp/quickstart.mdx new file mode 100644 index 00000000..611f319a --- /dev/null +++ b/apps/docs/content/zcp/quickstart.mdx @@ -0,0 +1,84 @@ +--- +title: "Deploy with a coding agent" +description: "Pick a Zerops starter with the AI Agent environment, open the hosted workspace, prompt Claude Code, deploy and verify a working app." +--- + +Pick a Zerops starter, open the hosted workspace, prompt Claude Code, watch it deploy and verify. By the end you have a real dev URL serving real content, with the agent showing you what it changed and how it verified. + +This Quickstart uses the hosted workspace because it has nothing for you to install. If you'd rather run ZCP from your local editor, see [Use ZCP locally](/zcp/setup/local-agent-bridge) — but for the first time through, stay hosted. + +## Prerequisites + +- A Zerops account with permission to create a project. [Sign up](https://app.zerops.io/) if you don't have one. +- A **Claude Pro or Max subscription**. Claude Code is the supported coding agent and uses your subscription to run the work. + +No local install needed — Claude Code runs preconfigured inside the hosted workspace. + +## Pick a recipe and deploy + +1. Open the [Zerops recipes catalog](https://app.zerops.io/recipes) and pick a recipe that matches what you want to build — Node, Bun, Python, Go, Rust, PHP, Laravel, NestJS, Next.js, Static, and more. +2. On the recipe page, select the **AI Agent** environment. The deploy button updates to include `-agent` (for example **Deploy laravel-showcase-agent**). +3. Click deploy. Zerops provisions the project. + +The **AI Agent** environment is what adds a `zcp@1` service alongside the recipe's runtime and managed services. The recipe ships it; you don't install anything. + +The rest of this page narrates the [Laravel showcase](https://app.zerops.io/recipes/laravel-showcase?environment=ai-agent) as one worked example because it exercises every part of the platform (runtime + queue worker + Postgres + Valkey + Meilisearch + object storage). With the **AI Agent** environment selected, the recipe provisions its app stack plus the ZCP service: + +- `appdev` — Laravel + Nginx (PHP 8.4) dev runtime; the agent edits and deploys code here +- `appstage` — Laravel + Nginx stage runtime; promotion target after `appdev` is verified +- `workerstage` — Laravel queue worker stage runtime +- `db` — managed PostgreSQL +- `redis` — managed Valkey (Redis-compatible) +- `search` — managed Meilisearch (full-text search) +- `storage` — S3-compatible object storage +- `zcp` — the hosted ZCP service that runs the MCP, the Cloud IDE, and Claude Code inside the project + +If you picked a different recipe, the service names will differ but the steps below work the same way — adapt the prompt to the stack you chose. + +## Open the workspace + +When provisioning finishes, open the **zcp** service in the Zerops dashboard and click the workspace link. A browser-hosted VS Code opens. The terminal already has Claude Code running, connected to the project's ZCP MCP. + +## Give the agent the prompt + +In the Claude Code terminal, paste a prompt that asks for a small concrete change against the recipe you picked. The example below targets the Laravel showcase; adapt the stack-specific bits if you started elsewhere: + +```text +Use ZCP to turn this starter into a small team notes app. Keep it simple: list notes, add a note, store notes in PostgreSQL, deploy it, verify the public URL, and tell me what changed. +``` + +A good run looks like this: + +1. **Reads project state.** The agent confirms the runtime scope (`appdev`) and the database (`db`). It doesn't ask you to install anything, find tokens, or configure the network — that's already done. +2. **Edits the dev runtime.** Files change in the editor pane. +3. **Deploys.** The agent runs a real deploy and waits for build + runtime to come up. +4. **Verifies against the real URL.** Fetches the public URL and probes it. If the requested behavior doesn't work, the agent reads logs, classifies the failure, and iterates. +5. **Reports.** A URL serving the notes app, plus a short summary of what it changed. + +If the agent gets stuck or asks a clarifying question, answer it as a developer would. A good agent explains the path it chose; you don't memorize tool calls. + +## Verify the result + +Open the URL the agent gave you. You should see the notes app: a list view, an "add note" form, and persistence across page reloads. + +Quick checks: + +- Add a note. Reload. The note should still be there. +- Look at the agent's summary. It should mention which runtime it deployed, where it verified, and any changes worth committing. + +Deploy success and verify success are not the same. Deploy success means the build finished and the runtime started; verify success means the requested behavior actually works. ZCP separates them on purpose. + +If something is missing or broken, see [Troubleshooting](/zcp/reference/troubleshooting). + +## Next steps + +- [How ZCP works](/zcp/concept/how-it-works) — the mental model and the full capability surface, by job. +- [Choose how finished work ships](/zcp/workflows/delivery-handoff) — when this small app is real, decide whether changes go through git, CI, or direct deploy. +- [Use ZCP locally](/zcp/setup/local-agent-bridge) — same flow, your local editor. + +## Gotchas + +1. **The deploy button label is environment-specific.** On the recipe page, picking the **AI Agent** environment changes the button to `Deploy -agent`. If the button still says `Deploy ` (no `-agent` suffix), the AI Agent environment is not selected and ZCP will not be added. Pick the environment first, then deploy. +2. **The first deploy goes through `appdev`.** If the agent skips dev and tries stage first, that is a wrong path — see [workspace shapes in Choose your workspace](/zcp/setup/choose-workspace#workspace-shapes) for why. +3. **A successful deploy with a broken page is not "done".** Verify the requested behavior, not just the runtime status. +4. **Production lives in a different project.** Do not run this Quickstart in a project that hosts your production app. See [Production boundary](/zcp/security/production-policy). diff --git a/apps/docs/content/zcp/reference/agent-workflow.mdx b/apps/docs/content/zcp/reference/agent-workflow.mdx new file mode 100644 index 00000000..78daf49b --- /dev/null +++ b/apps/docs/content/zcp/reference/agent-workflow.mdx @@ -0,0 +1,215 @@ +--- +title: ZCP agent workflow contract +description: Workflow contracts a coding agent operating against ZCP MCP follows — layers, bootstrap routes, develop flow, verification layers, failure categories, workspace shapes, delivery contracts, and the full MCP tool surface. +--- + +The canonical contract for an agent operating against ZCP MCP — what a session looks like, the layers it touches, the failures it classifies, the tools it calls, and what counts as done. The shape is explicit so you can predict what happens next, audit what happened, and recover when something goes wrong. + +Background — what ZCP is and how it fits into a project: [Infrastructure for Coding Agents](/features/coding-agents). Mental model: [How ZCP works](/zcp/concept/how-it-works). + +## Three layers a session touches + +Most workflow mistakes come from confusing these: + +| Layer | What it is | What changes go here | +|---|---|---| +| **Workspace** | Where the MCP runs — the `zcp@1` hosted service in the project, or the `zcp` binary on your laptop. Carries the integration token. In hosted mode, also runs the bundled agent CLI; in local mode the agent runs in your editor and talks to the MCP. | None — code changes don't target the workspace itself. | +| **Target runtime service** | The app's runtime — `appdev`, `appstage`, `app`. SSHFS-mounted into the hosted workspace, or in your local checkout for the local bridge. | App files, `zerops.yaml`, deploys. | +| **Managed services** | Databases, caches, search, queues, object storage. Provide connection details. | Schema migrations and data, never application code. | + +The most common mistake is targeting the workspace as the deploy destination. The workspace isn't the application — it's the place the agent operates from. + +## Bootstrap routes + +Before any code change, the agent reads what's already in the project and picks one of four routes. The agent should tell you which it picked. + +```mermaid +flowchart TD + start(["Agent reads project state"]) + q1{"Mid-bootstrap session
to resume?"} + q2{"Runtime services
already exist?"} + q3{"Request matches
a known stack?"} + resume(["resume"]) + adopt(["adopt"]) + recipe(["recipe"]) + classic(["classic"]) + start --> q1 + q1 -- yes --> resume + q1 -- no --> q2 + q2 -- yes --> adopt + q2 -- no --> q3 + q3 -- yes --> recipe + q3 -- no --> classic +``` + +| Route | Use when | Wrong signal | +|---|---|---| +| `adopt` | App runtime services already exist (most common, includes any recipe-based project) | Recreating services that exist, or treating the workspace as the target | +| `recipe` | Project is empty (or has only the workspace) and the request matches a known stack | Deploying the unchanged starter as if it were the finished product | +| `classic` | Project is empty and the request needs a custom service plan | Writing app code before service ownership is settled | +| `resume` | A previous session was interrupted mid-bootstrap | Starting from scratch without acknowledging existing state | + +Bootstrap completes when services are known and (in hosted) mounted; develop follows. + +## Develop flow + +A fixed sequence so progress is auditable. The loop closes when both verification layers pass. + +```mermaid +flowchart TD + scope["1. Name the runtime scope"] + build["2. Build the change"] + deploy["3. Deploy to in-scope runtime"] + serve["4. Serve the app"] + reach{"5. Reachability
verifies?"} + behave{"6. Requested behavior
verifies?"} + done(["✓ Done"]) + fix["7. Categorize failure,
fix with new evidence"] + scope --> build --> deploy --> serve --> reach + reach -- yes --> behave + reach -- no --> fix + behave -- yes --> done + behave -- no --> fix + fix --> deploy +``` + +1. **Name the runtime scope.** State which runtime the change targets (`appdev`, `appstage`, etc.). For `appdev` + `appstage` pairs, work scoped to `appdev` doesn't touch `appstage` unless promotion was explicitly requested. +2. **Build the requested change.** Code edits and `zerops.yaml` updates as needed. +3. **Deploy to the in-scope runtime.** First deploy uses the direct path; delivery style is decided later. +4. **Serve the app.** Start or restart the process if needed — dynamic dev runtimes may need explicit start/restart after deploy. +5. **Verify runtime reachability.** Platform-level health check. +6. **Verify the requested behavior.** Application-level check against the actual endpoint, UI, or stage URL. +7. **Fix and redeploy if needed.** Categorize the failure (below) and retry with new evidence, not the same attempt with hope. + +Steps 5 and 6 aren't interchangeable. A green deploy with a 500 on the relevant route isn't done. + +## Verification layers + +| Layer | What it checks | +|---|---| +| **Runtime reachability** | Platform health check — process alive, port bound, service answers. | +| **Requested behavior** | A real request against the actual endpoint, UI, or stage URL. Status codes alone aren't enough if the body is wrong. | + +Both must pass before a session reports completion. + +## Failure categories + +When something fails, the agent labels the failure with one of these tags. The tag drives the retry strategy and is what you read in the session log. This is the canonical home for the categories — other pages link here. + +| Category | What it means | Typical fix direction | +|---|---|---| +| `build` | Build phase failed (compile error, missing dependency, build command exit non-zero) | Inspect build log; fix `zerops.yaml` build steps or source | +| `start` | Container started but the process crashed or exited | Inspect runtime log; fix start command, init commands, or app entry | +| `verify` | Process started but reachability or behavior verification failed | Distinguish between unreachable port and wrong response | +| `network` | Service-to-service network problem (hostname unresolved, port closed, VPN-only access expected) | Confirm hostnames, ports, isolation settings | +| `config` | `zerops.yaml`, env vars, or service settings inconsistent with what the code expects | Reconcile config with code; document the contract | +| `credential` | Auth failure against a managed service or external API | Rotate or correct credentials; verify the credential surface (env var, git token, SSH key) matches what the failing call expects | +| `other` | Doesn't fit the categories above | Log details and escalate; agent shouldn't loop on unknown failures | + +Categorization is the discipline that turns retries into evidence-driven fixes. Repeating the same deploy with the same code on a `start` failure is a bug in the loop, not a recovery strategy. + +Recovery moves and symptom→category mapping: [Troubleshooting](/zcp/reference/troubleshooting). + +## Workspace shapes + +A project's runtime shape determines what "deploy" and "promote" mean during the develop flow. The agent identifies the shape from project state; you almost never pick directly. + +| Shape | Topology | Use case | +|---|---|---| +| `standard` | Dev runtime + stage runtime (e.g. `appdev` + `appstage`) | Separation between development and production-like environments | +| `dev` | One mutable development runtime | Fast experimentation, no separate staging | +| `simple` | Single runtime, no dev/stage pair | Small apps, static sites, simple APIs | +| `local-stage` | Local checkout + one Zerops runtime as the stage target | Local hot reload + deploys to a Zerops stage runtime | +| `local-only` | Local checkout, no Zerops runtime linked yet | Project is local-only or has only managed services | + +In `standard`, **stage is a promotion target only**. Changes scoped to `appdev` don't automatically reach `appstage`; promotion happens when the user asks for it. Conflating the two is the most common semantic error in `standard` projects. + +Decision context (hosted vs local, when each shape fits): [Choose your workspace](/zcp/setup/choose-workspace#workspace-shapes). + +## Delivery contracts + +After a verified deploy, the agent picks a contract that describes how *future* changes will be delivered. This isn't a flag affecting the current work — it's an agreement about what comes next. + +| Contract | Meaning | +|---|---| +| `auto` | Future work continues on the direct Zerops deploy path | +| `git-push` | Future work is committed and pushed to a configured remote; the resulting build is observed via webhook or GitHub Actions | +| `manual` | A human or external CI takes over delivery from here | + +For `git-push`, the agent observes the build result (webhook or Actions run) and records it before reporting the session complete. A push without a verified build result isn't a finished session. + +Plain-language framing of the same choice: [Choose how finished work ships](/zcp/workflows/delivery-handoff). + +## MCP tool surface + +The MCP exposes a fixed set of operations grouped into three tags. Host agents read the tags and apply policy — typically auto-allow read-only, require approval for destructive, ask once per operational tool. + +### Read-only — inspect state and configuration + +Safe to call without confirmation. Integration tokens can be scoped to read-only operations for CI jobs. + +| Tool | Purpose | +|---|---| +| `zerops_logs` | Stream runtime or build logs filtered by hostname, severity, time, search text | +| `zerops_events` | Project event timeline (deploys, builds, scaling, status transitions), scoped by hostname | +| `zerops_discover` | Read services, ports, env-var keys, current state from live platform reality | +| `zerops_verify` | Post-deploy reachability and HTTP-probe checks | +| `zerops_knowledge` | Surface ZCP-side guidance specific to current project state and step | + +### Destructive — mutate the project or its services + +Host agent should require user confirmation. Two operations carry an explicit ZCP-side gate (see Confirmation gates). + +| Tool | Purpose | +|---|---| +| `zerops_deploy` | Ship code through the build and deploy pipeline | +| `zerops_scale` | Adjust resources or HA/NON_HA mode on a managed or runtime service | +| `zerops_env` | Read/set/delete env variables at service or project scope | +| `zerops_delete` | Delete a service entirely (gated, requires named approval) | +| `zerops_manage` | Lifecycle operations — start, stop, restart, reload | + +### Operational — set up or coordinate work + +Host agent decides per team policy. These don't fit cleanly as read-only or destructive; they're setup-shaped. + +| Tool | Purpose | +|---|---| +| `zerops_recipe` | Apply a Zerops recipe to provision a known stack | +| `zerops_mount` | Manage SSHFS mounts of runtime services (hosted workspace with Cloud IDE only) | +| `zerops_subdomain` | Enable, disable, or inspect public subdomain access on a service | +| `zerops_workflow` | Coordinate the bootstrap → develop → deliver workflow state | +| `zerops_dev_server` | Run a long-lived dev process inside another project container (hosted workspace with Cloud IDE only) | + +## Confirmation gates + +Two operations carry an explicit ZCP-side confirmation gate because the loss isn't reversible from inside the conversation: + +- **Service deletion** — requires explicit user approval in the same conversation, by service name. Hosted ZCP additionally hard-blocks deleting the workspace it's running on (`SELF_SERVICE_BLOCKED`). +- **Wholesale service replacement** (import override) on a service with prior failed deploy history — first call is refused with a structured payload; second call must echo it back. + +Detail: [Tokens and credentials → Confirmation gates](/zcp/security/tokens-and-project-access#confirmation-gates-for-destructive-actions). + +## Practical rules + +- **Existing recipe projects** — adopt existing services rather than recreating. +- **Bootstrap before develop** — services must be known (and, in hosted, mounted) before develop starts. +- **First deploy uses the direct path** — delivery style is decided after the first deploy is verified. +- **Dynamic dev runtimes** — may need explicit start, restart, or process inspection after deploy. +- **Built-in webserver** — runtimes that ship with one don't need a separate dev-server step. +- **Runtime health vs requested behavior** — they aren't the same; both must pass. +- **Completion evidence** — a finished session shows a verified deploy on the in-scope runtime, *or* a clearly-stated blocker the user can act on. + +## Auditing a session + +A session is well-shaped if you can answer all of these from its log: + +- Whether existing or new services were used (route). +- Which runtime scope, workspace shape, and route were chosen. +- Where bootstrap ended and develop began. +- Whether the dev process started or was deliberately skipped. +- What URL, endpoint, or visible behavior proved success. +- Which delivery contract governs future changes. + +If any are unclear, the session under-explained itself. That's a fix-it-next-time signal, not a correctness failure. + +Step-by-step audit playbook with the evidence-coverage matrix and take-over decision tables: [Auditing agent work](/zcp/security/auditing-agent-work). diff --git a/apps/docs/content/zcp/reference/troubleshooting.mdx b/apps/docs/content/zcp/reference/troubleshooting.mdx new file mode 100644 index 00000000..6f2950c8 --- /dev/null +++ b/apps/docs/content/zcp/reference/troubleshooting.mdx @@ -0,0 +1,210 @@ +--- +title: "Troubleshooting" +description: "Diagnose ZCP problems by failure category or by symptom — deploys, logs, public URLs, tokens, VPN, workflow state." +--- + +Diagnose ZCP problems two ways. If the agent already classified the failure (`build`, `start`, `verify`, `network`, `config`, `credential`, `other`), [start from a failure category](#start-from-a-failure-category). If you see specific behavior but no clean classification, [start from a symptom](#start-from-a-symptom). For session-state confusion, jump to [Workflow state and session recovery](#workflow-state-and-session-recovery). + +## Start from a failure category + +Every failed deploy carries a structured classification: a category, a likely cause, a suggested next action. The agent reads classification before raw logs because the category points at the recovery path. Categories are **recovery-path-shaped**, not blame-shaped — `network` doesn't mean "the network is broken"; it means the first move is connectivity, VPN, SSH, DNS, or platform reachability rather than editing application code. + +### build + +The Zerops build pipeline failed before a runtime container could start. + +**Read first.** The build container's logs. Build logs live separately from runtime logs; runtime logs will not explain a failed build. + +**Recovery.** Fix `buildCommands`, dependency manifests, lock files, or the build working directory. If the failure is memory pressure, increase build resources or split the heavy build step. Redeploy after the fix. + +**Example.** "Build failed for `appdev` — `npm install` exited with `ENOENT: no such file or directory` on `package-lock.json`. Likely cause: lock file missing from the deploy. Suggested action: check `deployFiles` in `zerops.yaml`." + +### start + +The build succeeded, but the runtime did not reach a running state. + +**Read first.** Prepare logs when the failure is in `prepareCommands`; runtime logs when the runtime started briefly and crashed. The classification names which. If the agent surfaces warnings alongside an otherwise-successful operation, read them before treating the deploy as healthy — subdomain readiness and HTTP probe results show up there even when the deploy itself reports `FINISHED`. + +**Recovery path** (in this order): + +1. **Read the named log stream first.** Whatever the classification points at — prepare logs or runtime logs — that is the cause, not the alternative. +2. **Check `run.start` and ports.** Most "started briefly and crashed" failures are the start command exiting non-zero, not binding the configured port, or binding `127.0.0.1` instead of `0.0.0.0`. +3. **Check env-var references.** Missing `${db_*}` or `${redis_*}` resolution leaves the runtime crashing with "undefined" connection strings. See [Environment variables](/guides/environment-variables). +4. **Check `prepareCommands` for missing packages or wrong distro names.** Alpine vs Debian package names trip up Python and PHP runtimes most often. + +**Example.** "Deploy of `appdev` failed at start. Cause: `EADDRINUSE` on port 3000 — another command in the start sequence is already binding the port. Suggested action: confirm `run.start` binds the configured port and that only one command owns it." + +### verify + +The deploy finished, but a post-deploy health check did not pass. + +**Read first.** The first failing check name and its detail. For HTTP probe failures, the response status and body text. For service-level failures, recent error logs. + +**Recovery.** Inspect the failing check. For `http_root` 5xx, read runtime logs at request time and fix the application error. For "subdomain access not enabled," recover the public route before changing app code. + +**Example.** "Deploy of `appdev` succeeded but verify failed: HTTP probe returned 502 against the public URL. Cause: runtime is up, but the listener is not on `0.0.0.0`. Suggested action: read `zerops.yaml` `run.ports[]` and confirm the start command binds `0.0.0.0`, not `127.0.0.1`." + +Subdomain-readiness signals can appear as warnings the agent surfaces alongside an otherwise-successful operation (an `enable subdomain` call that returned `FINISHED` may include "URL not ready after 10s"). Read warnings before treating the URL as live. + +### network + +ZCP could not reach the platform or source container at the transport layer. No build was triggered. + +**Read first.** The transport-layer error string in the failure detail. + +**Recovery.** Confirm the source service is running and reachable. In a local workspace, bring up the Zerops VPN. If the SSH process was killed mid-transfer, scale source container memory. If the failure is a platform timeout, wait for the platform path to recover before retrying. + +**Example.** "Deploy of `appdev` failed at the transport layer: `signal: killed` during the SSH transfer. Likely cause: the source container ran out of memory mid-transfer. Suggested action: scale `appdev` RAM up and retry." + +### config + +A pre-deploy check rejected the request before the platform accepted the deploy. + +**Read first.** The structured error detail — the field path, API metadata, or suggestion attached to the rejection. + +**Recovery.** Fix the relevant `zerops.yaml` setup block, base/runtime name, `deployFiles`, or prerequisite. For setup-block mismatches, use the setup name declared in `zerops.yaml`, not the hostname. Redeploy when the config matches the workspace and deploy direction. + +**Example.** "Deploy refused: `INVALID_ZEROPS_YML` — `setup` block named `prod` was not found. Cause: the `zerops.yaml` declares only `dev`. Suggested action: pass the existing setup name or add the missing block." + +### credential + +An auth token, git credential, or SSH credential was missing or rejected. Recovery is regenerate-or-reconfigure, not connectivity. + +**Read first.** Which credential the failure names — Zerops API token, git push credential, SSH credential. + +**Recovery.** Regenerate the rejected token with the required project or repository scope. Re-run the relevant credential setup so the token, the auth file, and the remote URL line up. Retry only after the agent confirms the expected credential is present. + +**Example.** "git push from `appdev` failed: `Authentication failed`. Likely cause: `GIT_TOKEN` is missing on the source container. Suggested action: re-run git-push setup; the agent will provision the token." + +### other + +The classifier ran, but no known recovery signal matched. Treat this as "read the raw evidence", not as a diagnosis. + +**Read first.** The raw `Reason` field. The absence of a specific signal is itself useful — the agent should stop trying to fit the error into a known card. + +**Recovery.** Read the raw event timeline for the affected service. Read logs for the phase named by the raw status. If the same raw status repeats, report it with the exact `Reason` and recent log lines. + +## Start from a symptom + +Pick the section that matches what you're seeing. Symptom tables map to the most common shapes; the failure-category column maps back to the [category sections above](#start-from-a-failure-category) for full recovery detail. + +### Deploy errors + +| Symptom | Likely category | Next move | +|---|---|---| +| Build container exits non-zero — "command not found", missing module, npm 404 | `build` | Read `buildCommands` in `zerops.yaml`. Add missing packages or fix typos. | +| Container OOM-killed during build or `signal: killed` during deploy | `build` / `network` | Scale build or source container RAM up and retry. | +| Runtime crashes immediately — `EADDRINUSE`, `Cannot find module`, missing env var | `start` | Read runtime logs scoped to the failed deploy. The classification names the likely cause. | +| Database connection refused during init | `start` | Confirm the DB service is running; confirm env-var references (`${db_*}`) resolve. | +| `Authentication failed` on git push | `credential` | Check the git credential — `GIT_TOKEN` (hosted) or your local git credential helper (local). | +| Preflight refuses with an `INVALID_ZEROPS_YML` or setup-mismatch error | `config` | Read the field-level reasons in the rejection — fix the rejected fields and retry. | + +For any of these, the diagnostic next step is the same — ask the agent to read the deploy events, build logs, and runtime logs: + +```text +Show me the build logs for the last failed deploy of appdev, and read the failure classification. +``` + +Build logs and runtime logs are different streams. A build failure does not show in runtime logs, and a runtime crash does not show in build logs. If the agent pastes the wrong stream, the diagnosis will miss. + +### Verify errors + +A deploy can succeed (build green, runtime running) and still fail to verify. Verify is a separate gate — reachability plus the requested behavior. Some common shapes: + +| Symptom | Likely cause | Next move | +|---|---|---| +| Service status is `RUNNING` but the route returns 500 / 404 | The app started but the requested behavior is broken. | Read runtime logs at request time; the agent confirms whether the route is wired up and the dependencies it needs. | +| HTTP probe on the public URL returns a connection error | Subdomain access is still propagating, or the runtime is not bound to `0.0.0.0`. | Wait 30-60 seconds and retry. If it stays broken, see Public URL errors below. | +| Service status is `READY_TO_DEPLOY` after a deploy | Build succeeded but the runtime never started (start command failed). | Read runtime logs scoped to the start phase — the start command itself is the cause. | +| `RUNNING` but log output shows continuous restart | The start command exits and is being restarted by the platform. | Read runtime logs over the last few minutes; the exit reason is in the logs. | + +### Public URL errors + +Public access is auto-enabled on the first deploy for eligible services. If the URL still does not work after deploy, work through the symptoms in order — the platform may still be propagating, or the service may not be eligible for public access. + +| Symptom | Likely cause | Next move | +|---|---|---| +| URL returns "Not Found" or empty response right after first deploy | Subdomain is still propagating. | Wait 30-60 seconds and re-fetch. The agent's verify step retries automatically. | +| Subdomain stays disabled even after a successful deploy | The service is a worker (no HTTP) or a non-eligible mode. | Workers have no public URL by design. Confirm the service is meant to serve HTTP. | +| 502 Bad Gateway persists after deploy + wait | The runtime is not bound to `0.0.0.0`, or the port in `zerops.yaml` does not match the bound port. | Read `zerops.yaml` `run.ports[]`. Confirm the start command binds the same port on `0.0.0.0`, not `127.0.0.1`. | +| Custom domain not resolving | DNS propagation, or the domain is not configured on the service. | See [Public access](/guides/public-access) for the full domain setup. | + +If the service really should have a public URL and auto-enable did not catch it, you can ask the agent to enable subdomain access explicitly: + +```text +Enable subdomain access on appdev and verify the URL responds. +``` + +The agent runs the equivalent operation, waits for HTTP-readiness, and reports the URL. + +### Token errors + +Token rejection happens at startup. ZCP refuses to come up with a token that has the wrong shape, and the error names the specific reason. + +| Symptom | Likely cause | Next move | +|---|---|---| +| `Token accesses N projects; use project-scoped token` | A multi-project (or full-access) token was supplied. | Generate a project-scoped token in the Zerops dashboard and replace the value. See [Tokens and credentials](/zcp/security/tokens-and-project-access). | +| `Token has no project access` | The token authenticates a user but cannot reach any project. | Grant the token project access (or generate a new project-scoped token) and replace the value. | +| `No authentication found: set ZCP_API_KEY or log in with zcli` | No token is reaching ZCP. | Add `ZCP_API_KEY` under the `env` block of `.mcp.json`, or run `zcli login `. | +| API replies with 401 / `AUTH_TOKEN_EXPIRED` | The token was revoked or has expired. | Generate a new project-scoped token, replace the value, and restart the agent. | + +After replacing the token in `.mcp.json`, restart the agent. ZCP reads `ZCP_API_KEY` once at process startup; the live session still holds the old value in memory. + +### VPN errors (local install) + +The local agent bridge reaches Zerops managed services over the project [VPN](/references/networking/vpn). The hosted workspace does not need a VPN — it is already on the project's private network. These symptoms only apply to the local bridge. + +| Symptom | Likely cause | Next move | +|---|---|---| +| `connection refused` against a managed service hostname (e.g. `db`, `redis`) | VPN is not up, or it dropped after sleep / network change. | `zcli vpn up ` — bring the tunnel back. | +| `no route to host` for `db.zerops` or similar | Same as above; the search domain only resolves through the VPN. | `zcli vpn up `. | +| Local app starts but cannot connect to Postgres / Valkey / storage | VPN is up but the `.env` is stale, or VPN is down. | First check `nc -zv db 5432 -w 3` (or the right port). If that fails, restart the VPN. If it succeeds, regenerate the `.env`. | + +VPN drops with sleep, network change, and idle timeouts. Treat re-running `zcli vpn up` as a routine recovery, not a setup error. + +### Local install errors (`zcp init`, `.mcp.json`, `.env`) + +Local-bridge setup has a few moving pieces — the binary, the generated config, the token, and the `.env`. The most common stumbles: + +| Symptom | Likely cause | Next move | +|---|---|---| +| Re-running `zcp init` made the agent stop working | `zcp init` rewrites `.mcp.json` from the template; your `ZCP_API_KEY` is gone. | Re-add the `env` block with `ZCP_API_KEY` and restart Claude Code. See [Tokens and credentials](/zcp/security/tokens-and-project-access). | +| Claude Code does not list the `zerops` MCP server | Claude Code launched from the wrong directory. | Quit and relaunch from the project root that contains `.mcp.json`. | +| Local app reads stale env values | `.env` is a snapshot taken at generation time. | Regenerate after any project env-variable change — ask the agent to re-run the env-file generator. | +| `zcp` not found on `PATH` after install | The install script wrote to `~/.local/bin` and that is not on `PATH`. | Add `~/.local/bin` to `PATH` in your shell rc and reload. | + +Re-running `zcp init` is safe for everything except `.mcp.json` — `CLAUDE.md` preserves your edits outside the managed markers, but `.mcp.json` is rewritten clean. If you re-init, treat re-adding the token as routine. + +## Workflow state and session recovery + +"The agent forgot," "no project found," "session lost" — almost all session-state errors recover with a status read. + +```text +Read ZCP status and tell me where this project stands. +``` + +Status rebuilds the agent's view from live platform state — services, their state, recent deploys, recent failures — without you re-explaining the project. The right recovery move when a long Claude Code session has lost the thread, you closed the browser tab or restarted the editor mid-task, the agent answers vaguely about services, or you're not sure whether the last deploy actually succeeded. + +| Symptom | Likely cause | Next move | +|---|---|---| +| Agent says "no project found" or "I have no context" mid-conversation | The agent's chat memory drifted from real platform state. | Ask for status (above). It reads live state and rebuilds the picture. | +| Agent loops on the same failed deploy without progressing | Five attempts without progress is the cadence to expect — see [Build and verify an app](/zcp/workflows/build-and-verify-app). | Stop the loop and ask the agent to summarize what it has tried and what evidence is still missing. | +| Agent picks the wrong service when you said "the runtime" | In a dev/stage project there are two runtimes; ambiguous prompts can land on the wrong one. | Name the hostname explicitly: `Deploy appdev` rather than `deploy the runtime`. | + +Status is not just for cold starts. Any time the conversation drifts from the project, asking ZCP for current state realigns the agent with real state in one prompt. If status itself fails — the agent reports "cannot reach ZCP" or returns a token error — see [Token errors](#token-errors). + +## Gotchas + +- **Build logs and runtime logs are different streams.** A build failure shows in build-container logs; a runtime crash shows in runtime-container logs. The wrong stream returns nothing useful. +- **Restart the agent to refresh the MCP tool list.** The agent reads ZCP's tool catalog at startup. If ZCP's surface changes (new install, version update) and the agent can't find an operation, quit and relaunch so it re-introspects. +- **VPN drops are routine, not setup errors.** Sleep, network change, and idle timeouts drop the tunnel. `zcli vpn up ` brings it back. The error message ("connection refused" / "no route to host") looks like an app bug but is almost always a network move. +- **Re-running `zcp init` doesn't preserve the token in `.mcp.json`.** The regenerated file ships without `ZCP_API_KEY`. Re-adding the `env` block is part of re-init, not a separate setup step. + +## Related + +- [Build and verify an app](/zcp/workflows/build-and-verify-app) — where these failures surface in the deploy/verify loop. +- [Auditing agent work](/zcp/security/auditing-agent-work) — review failed deploys and the classification attached to them. +- [Tokens and credentials](/zcp/security/tokens-and-project-access) — full token shape, where it lives, and how it relates to git credentials. +- [VPN](/references/networking/vpn) — Zerops project VPN reference (used by the local agent bridge). +- [Public access](/guides/public-access) — public URL platform behavior, custom domains, and DNS. +- [Deployment lifecycle](/guides/deployment-lifecycle) — the build, deploy, and event surface ZCP reads from. diff --git a/apps/docs/content/zcp/security/auditing-agent-work.mdx b/apps/docs/content/zcp/security/auditing-agent-work.mdx new file mode 100644 index 00000000..11899f49 --- /dev/null +++ b/apps/docs/content/zcp/security/auditing-agent-work.mdx @@ -0,0 +1,157 @@ +--- +title: "Auditing agent work" +description: "Review ZCP agent work through Zerops events, logs, deploy attempts, verification results, and git history." +--- + +Review ZCP agent work through Zerops events, logs, deploy attempts, verification results, and git history. **Platform-side actions leave platform-native evidence; agent-side decisions, prompt content, and shell-driven file edits don't.** What you can audit and what you can't are spelled out below — read the evidence-coverage matrix before you ask "did the agent do X" expecting the answer to live in Zerops. + +The page assumes a development session has happened. The agent reported it finished, a deploy looped, you stepped away — the platform recorded its share either way. The audit is reading what's there in the right order, knowing what isn't. + +## Evidence coverage + +| Action | Where evidence exists | What's missing | +|---|---|---| +| Deploys (build status, appVersion, failure classification) | Zerops project events + deploy/verify history | Nothing — the platform record is canonical. | +| Runtime behavior (crashes, log lines, restarts) | Runtime logs scoped by hostname, severity, time | Pre-deploy local stdout if your app prints before being deployed. | +| Lifecycle (start, stop, restart, scale, subdomain enable/disable) | Project events timeline | The agent's reasoning for choosing the action — that lives in the chat client. | +| Env-var changes (set, delete, update) | Project events; current values via discover | Per-call before/after diff isn't separately recorded — read the discover snapshot before and after. | +| Service deletion and import override | Project events + the resulting service-stack changes | The structured `wouldDestroy`/confirmation exchange persists only in the chat-client transcript. Once executed, the destroyed state itself is gone — roll back via [Backup](/features/backup), not the audit log. | +| Git-push delivery commits | Git history on the configured remote | Same as any other committer — `git log`, `git blame`, your PR review. | +| **Agent-side decisions, prompt history, planning** | The chat client's transcript | Not recorded by ZCP. If the chat is closed without export, that reasoning is gone. | +| **File edits the agent makes via a shell terminal in the workspace** | Filesystem state on the runtime container | Per-edit diff isn't platform-recorded. Use `git status` in the runtime mount or commit incrementally to capture intent. | +| **Generated `.env` files (local agent bridge)** | The file in your project directory | ZCP wrote it from current project env; per-call detail isn't separately logged. | +| **Hosted code-server URL access** (who hit the workspace, when) | The Zerops platform's access logs for the public subdomain | Not surfaced as part of the ZCP-side audit. See [Public access](/features/access). | +| **Browser-helper actions** in the hosted workspace (clicks, navigation) | The agent's chat transcript only | Not platform-recorded. | + +Many gaps are platform boundaries, not silent failures. When the gap is load-bearing for your review (e.g. compliance), supplement the platform record with chat-client transcripts, git history, and your own commit discipline. + +## Read in this order + +The four platform-side streams have an order: + +1. **Project events** — timeline of deploys, builds, scaling, status transitions. Scope by service hostname; the project-level view mixes everything. +2. **Runtime logs** — explain why a failure happened, scoped by hostname, severity, time, search. +3. **Deploys and verifies** — the canonical "did the agent ship something, and did it work" record. +4. **Git history** (when delivery uses git-push) — commits the agent pushed, reviewed like any other diff. + +When a session has gone sideways and you want the agent's current view of the project, **ZCP status** rebuilds the picture from live platform state in one prompt. + +Skipping events and starting from logs gives you a wall of stack traces with no framing. + +## Project events + +The project event timeline is what the platform itself recorded — every deploy, build, scaling action, status transition. Ask the agent for it scoped to the service you care about: + +```text +Show me the last 20 events for appdev. +``` + +```text +What happened on appdev between 14:00 and 15:00 today? +``` + +The agent reads the timeline back sorted by time, with each entry tagged by event type, action, status, and — for failures — a structured failure classification. See [Deployment lifecycle](/guides/deployment-lifecycle) for what each event type means; failure classification points at [Troubleshooting → Failure categories](/zcp/reference/troubleshooting#start-from-a-failure-category). + +Filter by service hostname every time. The project-level view mixes events from every service and every old build container — for an audit, you want the runtime in scope and nothing else. + +The timeline answers: did the agent deploy this service today (and how many times)? Did any deploys fail (and with what classification)? Did the agent restart, scale, or change the subdomain? + +## Runtime logs + +Once events frame what happened, runtime logs explain why. Ask scoped by hostname, severity, time window, and search text: + +```text +Show me the ERROR-level logs for appdev in the last hour. +``` + +```text +Find any log line on appdev mentioning "migration" since 14:00. +``` + +```text +Tail appdev runtime logs since the last deploy. +``` + +A failed build prints in the build container; a runtime crash prints in the runtime container. If the timeline says `BUILD_FAILED`, ask for build logs. If it says `DEPLOY_FAILED` or the service crashed after a green build, ask for runtime logs. `ERROR` is the right starting severity filter; pull `WARNING` next for a fuller picture; pull all severities only when you have a specific message in mind. + +Logs are the application's own stdout and stderr — the agent doesn't paraphrase them in transit. + +## Deploys and verifies + +Deploys and verifies are the canonical "did the agent ship something, and did it work" record. They persist on different surfaces: deploy and build events are durable platform records you can reread weeks later; verify results live in the agent's response and ZCP's short-lived session memory, which is cleaned over time. For audit you can revisit, capture the verify response in the chat transcript or rerun verify on demand. + +Each deploy carries a build status, an appVersion status, and — for failures — a structured classification. Each verify run records service status, error log presence, and the HTTP probe outcome. A "successful" deploy with a failed verify is a real signal — the build finished and the runtime started, but the requested behavior didn't pass. The audit needs both halves. + +```text +Show me the last 5 deploy attempts for appdev with their outcomes. +``` + +```text +What was the failure classification on the appdev build at 14:32? +``` + +```text +Did the verify on appdev pass after the last deploy? +``` + +A looping deploy (the agent retried the same build several times without progress) shows up as repeated `BUILD_FAILED` entries with the same `failureCause` and no new commit between them. That's your cue to stop and read the cause yourself rather than ask the agent for another retry. + +## Git history (when delivery uses git-push) + +When the agent's delivery answer is "push to git, then build from there", edits land as commits on a configured remote. The audit shifts: the agent is just another committer, and the diffs review the same way a coworker's would. + +```bash +git log --oneline -20 +git diff main...HEAD +gh pr view +``` + +When git-push delivery has a build integration (Zerops dashboard webhook or GitHub Actions), each push fires the next deploy; the deploy/verify history above shows what the build did with that push. Cross-referencing the two streams is how you confirm "the commit landed and the deploy that ran from it succeeded". Platform side: [GitHub integration](/references/github-integration). + +When the agent kept deploying directly, no git evidence exists. When delivery was handed off to you or your CI, git review is your call. See [Choose how finished work ships](/zcp/workflows/delivery-handoff). + +## Take over from the agent + +Auditing leads to a decision: keep the agent running, take the rest manually, or roll back. + +**Mid-session — agent looks stuck.** Get the agent's current view from ZCP first: + +```text +Read ZCP status before doing anything else. +``` + +ZCP status returns the current scope, last actions, and what's still pending — rebuilt from live platform state plus the session record. Compared to scrolling the transcript, this reads from reality. If the agent's chat-side picture has drifted, status is what realigns it. + +| Situation | Action | +|---|---| +| Agent has a clear next step but is hesitating | Tell it to proceed. Status told you the path is intact. | +| Agent is looping on the same failure | Stop the loop. Read the failure classification and last logs yourself, give the agent the corrected approach, or take over manually. Five attempts without progress is the cadence to expect — see [Build and verify an app](/zcp/workflows/build-and-verify-app). | +| Scope is wrong (agent targeting `appstage` when you asked for `appdev`) | Correct the scope explicitly. The agent picks up the new value on the next call. | +| Session has drifted past usefulness | End the session. The deploys and verifies it ran are recorded; resume from the same project on a fresh start. | + +**After a normal close — keep, take over, or revert.** A session that auto-closed has a clean deploy and a passing verify on every service in scope. The audit decides: + +| Decision | What to do | +|---|---| +| Keep the changes | Nothing further is required. The deploys are live, the verify passed. | +| Take over manually for the next change | Switch the delivery answer to "hand off to your CI or a human" if you want ZCP to stop initiating deploys. See [Choose how finished work ships](/zcp/workflows/delivery-handoff). | +| Revert via git | When delivery uses git-push, revert the commit on the remote and let the build integration ship the previous state. Standard `git revert` workflow. | +| Restore from backup | When data was lost, use the platform's [backup and restore](/features/backup) — that's the data-safety net, not ZCP. | + +Before any irreversible follow-up — a destructive import override, a service deletion — re-read [Tokens and credentials → Confirmation gates](/zcp/security/tokens-and-project-access#confirmation-gates-for-destructive-actions). The audit you just ran is the diagnosis those gates expect you to have done first. + +## Gotchas + +- **Filter events by service hostname every time.** The project-level view mixes builds from every service and every retry. Without the hostname filter you'll read someone else's failure and diagnose the wrong thing. +- **Read status before changing anything when taking over.** The chat transcript isn't the source of truth. Status rebuilds the agent's view from live platform state — a single call confirms whether scope, last action, and pending work match what you remember. +- **Build logs and runtime logs are different streams.** A failed build never appears in runtime logs; a runtime crash never appears in build logs. The timeline's failure category points at the right stream. +- **The agent's diffs are like any committer's.** When delivery uses git-push, don't treat the agent's commits as a separate review surface. Use `git log`, `git diff`, your normal PR review. +- **Backup is the data safety net, not ZCP.** Audit and roll-back via git cover code; if data was lost, [Backup](/features/backup) is what gets it back. The audit trail can show you a destructive operation happened — it can't undo it. + +## Related + +- [Deployment lifecycle](/guides/deployment-lifecycle) — the canonical reference for events, build phases, and the timeline shape this page reads. +- [How ZCP works](/zcp/concept/how-it-works) — the recovery model that makes status the right take-over primitive. +- [Tokens and credentials → Confirmation gates](/zcp/security/tokens-and-project-access#confirmation-gates-for-destructive-actions) — audit before any destructive override. +- [GitHub integration](/references/github-integration) — when reviewing PRs the agent pushed. +- [Backup](/features/backup) — recovering from data loss surfaced during the audit. diff --git a/apps/docs/content/zcp/security/production-policy.mdx b/apps/docs/content/zcp/security/production-policy.mdx new file mode 100644 index 00000000..370dba7a --- /dev/null +++ b/apps/docs/content/zcp/security/production-policy.mdx @@ -0,0 +1,70 @@ +--- +title: "Production boundary" +description: "Production lives in a separate Zerops project that doesn't have a zcp service; promotion runs via CI or a release pipeline." +--- + +Production lives in a separate Zerops project that doesn't have a `zcp` service. ZCP is for development and staging — packaging a verified ZCP project for production handoff goes through [Package a running service](/zcp/workflows/package-running-service), which produces a re-importable bundle whose `buildFromGit:` URL points at the same source repo. Promotion runs via CI or a release pipeline. + +This is **policy, not enforcement**. The platform doesn't stop you from adding ZCP to a production project; the project-scoped token enforces isolation. Treat the dev/staging boundary as load-bearing because crossing it puts the agent inside production, not because Zerops will refuse the configuration. + +## Why public preview implies dev/staging + +ZCP ships under the public preview model: + +- **Setup details may change.** `.mcp.json`, the configuration surface, the recipe environment menu, the available agents — any of these can shift between previews. Pin ZCP to development and staging projects so a change between releases doesn't put a production service in transition. +- **Errors surface against real platform calls.** Public preview means real deploys, real services, real bills, and a real platform — not a sandbox. When something goes wrong, the platform shows what actually happened. That value is the reason you don't aim it at production yet. + +## Separate production project + +Keep production in a Zerops project that doesn't have a `zcp` service. Separate the agent's workspace from the running production app at the project boundary, not at the service boundary inside one project. + +Why a separate project: + +- **Project is the security boundary.** ZCP authorizes a single project per token (see [Trust model](/zcp/security/trust-model)). A token scoped to your development project can't reach production by accident. +- **Env values stay clean.** Production env values live in production's project; development env values live in development's project. Mixing them muddies which value the agent's local `.env` actually pulls in. +- **Scaling and backup policies differ.** Production typically runs HA-mode services with longer backup retention; development typically runs NON_HA with shorter retention. Separation lets [scaling](/features/scaling) and [backup](/features/backup) policies stay distinct without compromise. + +In practice that means at least two projects: a development (or dev + staging) project where ZCP runs and the agent works, and a production project that doesn't have ZCP added, where deploys come from your CI or release pipeline. + +## Stage as proof + +Stage isn't a formality. It's where the agent proves the change works on the same shape your production runs — same runtime version, same managed services, same deploy pipeline — before promotion. + +Use stage as the production-shape rehearsal: + +- The agent develops on dev (`appdev` in the `standard` workspace shape). +- After dev verifies, the agent cross-deploys to stage (`appstage`). +- Stage runs the real start command, not the dev hot-reload. It exercises the same build, deploy, and verify path production will use. +- A green stage is the signal that the change is ready to leave the development project. + +Skip stage and you skip the layer where production-shaped failures show up. + +## Promotion path + +The handoff from development to production is **outside ZCP**. When stage verifies, ZCP's role in the development project is done. From there: + +- A team-owned **CI pipeline** picks up the merged commit (or a release tag) and deploys to the production project. +- Or a human runs `zcli push` against production manually, using a token scoped to production. +- Or [GitHub Actions](/references/github-integration) on the production repo authenticates with a separate `ZEROPS_TOKEN` scoped to the production project (a different secret from the development workflow's). + +The agent doesn't bridge the two projects. ZCP binds at startup to whatever single project its token resolves to — a token scoped to your development project can't operate on production. Promotion is a deliberate human or pipeline step. + +Broader CI/CD reference: [CI/CD with Zerops](/guides/ci-cd). + +## What not to do + +| Anti-pattern | Why it's wrong | +|---|---| +| Add a `zcp` service to your production project | The agent gets project-scoped power over production | +| Reuse a development `ZCP_API_KEY` against production | The token is project-scoped — fails outright or breaks the boundary | +| Treat dev verification as production approval | Dev is hot-reload; stage is the production-shape rehearsal. Skip stage and you skip the rehearsal. | +| Run the agent's deploys directly into production from a development session | Production deploys come from the production project's pipeline, not the development workspace | +| Suppress the dashboard's preview warning locally | The warning is canonical platform guidance | + +## Next steps + +- [Trust model](/zcp/security/trust-model) — the project boundary that makes this separation enforceable. +- [Tokens and credentials](/zcp/security/tokens-and-project-access) — how project-scoped tokens prevent cross-project leakage. +- [Auditing agent work](/zcp/security/auditing-agent-work) — review what the agent did in development before promoting. +- [Backup](/features/backup) — the production data safety net that operates outside ZCP. +- [Build and verify an app](/zcp/workflows/build-and-verify-app) — the dev/stage loop this policy assumes. diff --git a/apps/docs/content/zcp/security/tokens-and-project-access.mdx b/apps/docs/content/zcp/security/tokens-and-project-access.mdx new file mode 100644 index 00000000..88e6d508 --- /dev/null +++ b/apps/docs/content/zcp/security/tokens-and-project-access.mdx @@ -0,0 +1,147 @@ +--- +title: "Tokens and credentials" +description: "Project-scoped Zerops tokens, git credentials, and the confirmation gates ZCP enforces before destructive actions." +--- + +ZCP enforces project boundary at three layers — token shape (one project, exactly), where the token lives (env-injected for hosted, in `.mcp.json` for local), and per-action gates on operations that aren't reversible from inside the conversation. For the broader trust framing see [Trust model](/zcp/security/trust-model). + +## Required token shape — one project, full access + +ZCP needs a Zerops API token that resolves to **exactly one project** at startup. In the dashboard, the integration-token type that produces this is **Custom access per project** scoped to a single project — that's the recommended shape. The other two types (*Full access to all projects* and *Read access to all projects*) typically resolve to more than one project on any non-trivial account; ZCP refuses to start when the resolved project count isn't one. + +To generate one: + +1. Open [Settings → Access Tokens Management](https://app.zerops.io/settings/token-management). +2. **Create Token**, name it (e.g. `zcp-`). +3. Pick **Custom access per project**. +4. Add the project the agent will operate against, set permission to **Full access**, create. +5. Copy the value — Zerops shows it only at creation time. + +The token's blast radius equals the project. Other projects in the organization, account-level settings, and billing stay out of reach. See [Roles & Permissions](/features/rbac#integration-tokens) for how Zerops scopes integration tokens at the platform layer. + +## Rejected token shapes + +ZCP validates the token at startup and refuses the wrong shapes: + +| Token shape | What ZCP does | Why | +|---|---|---| +| Multi-project (e.g. full-access) | Refuses with `TOKEN_MULTI_PROJECT` | One ZCP connection equals one project; ZCP won't pick which one. | +| No project access | Refuses with `TOKEN_NO_PROJECT` | The token authenticates a user but reaches no project. | +| Read-only token | Fails on the first mutation | Passes startup auth; Zerops rejects the first deploy / env write / build call with a permission error. | +| Expired or revoked | Refuses with `AUTH_TOKEN_EXPIRED` or `AUTH_REQUIRED` | The token no longer authenticates against the Zerops API. | + +A multi-project rejection looks like this in the agent's view: + +```text +Token accesses 4 projects; use project-scoped token +Recovery: Create a project-scoped token in Zerops GUI or set project via zcli scope +``` + +The fix is always the same: generate a per-project token and replace the value, or scope `zcli` to the target project before starting ZCP. + +## Where the token lives — hosted vs local + +ZCP reads `ZCP_API_KEY` from environment at startup. It doesn't write the token anywhere else, doesn't exchange it for a derived credential. Where the env var comes from depends on which path you're on: + +| Workspace | Where `ZCP_API_KEY` comes from | Who provisions it | +|---|---|---| +| Hosted workspace | The `zcp` service container env | Zerops platform — injected automatically when the workspace boots | +| [Local agent bridge](/zcp/setup/local-agent-bridge) | The `env` block of `.mcp.json` in the project directory | You — added manually after `zcp init` | + +In the hosted workspace, opening the workspace from the Zerops dashboard is enough. The container starts with `ZCP_API_KEY` populated; Claude Code launches without asking. + +In the local bridge, `zcp init` writes a token-less `.mcp.json` template; you add the `env` block: + +```json +{ + "mcpServers": { + "zerops": { + "command": "zcp", + "args": ["serve"], + "env": { + "ZCP_API_KEY": "" + } + } + } +} +``` + +Gitignore `.mcp.json` so the token doesn't leave the machine. Each project directory has its own `.mcp.json` and its own token — switching projects means switching directories, not editing one shared file. + +## Git credentials — separate from `ZCP_API_KEY` + +Three names, three jobs — keeping them straight prevents most credential confusion in [delivery handoff](/zcp/workflows/delivery-handoff): + +| Name | What it authorizes | Where it lives | +|---|---|---| +| `ZCP_API_KEY` | ZCP MCP itself, against the Zerops API | Container env (hosted) or `.mcp.json` env block (local) | +| `GIT_TOKEN` | A git push from a ZCP-managed environment to a remote (GitHub, GitLab) | Hosted: project env var on the ZCP service. Local: not applicable — your git credential helper handles it. | +| `ZEROPS_TOKEN` | A GitHub Actions workflow running `zcli` against Zerops | A repository secret in GitHub Actions | + +`GIT_TOKEN` only matters when delivery uses git-push and ZCP is the one pushing (the hosted path). In the local bridge, your own git CLI talks to the remote — SSH keys or credential manager already handle authentication, and ZCP doesn't need a credential of its own. If `GIT_TOKEN` is present in the hosted workspace's project env when you run git-push setup, ZCP reuses it instead of asking for a new credential. + +`ZEROPS_TOKEN` is a **Zerops API token, not a GitHub token** — the Actions workflow uses it to authenticate `zcli` against Zerops. It can be the same project-scoped token already behind `ZCP_API_KEY` (one credential, one rotation surface) or a separate token if your team prefers credential separation. The agent prepares the right `gh secret set` command for your environment: + +```bash +# Hosted workspace — read ZCP_API_KEY from the container env +gh secret set ZEROPS_TOKEN -b "$ZCP_API_KEY" -R / + +# Local bridge — extract ZCP_API_KEY from .mcp.json +gh secret set ZEROPS_TOKEN -b "$(jq -r '.mcpServers.zerops.env.ZCP_API_KEY' .mcp.json)" -R / +``` + +Run the command in a terminal authenticated to the GitHub repository (`gh auth login` first if needed). The literal token value never crosses the MCP wire — the shell expands `$ZCP_API_KEY` or `jq` reads `.mcp.json` at command time. + +## Rotation + +Rotate in the Zerops dashboard, then propagate to the consuming surface: + +- **Hosted workspace** — the project env value updates; ZCP picks up the new value the next time the workspace boots or the `zcp` service restarts. +- **Local bridge** — paste the new token into the `env` block of `.mcp.json`, then restart Claude Code so the new ZCP process inherits the updated env. +- **GitHub Actions secret** — `gh secret set ZEROPS_TOKEN ...` (or the GitHub UI) replaces the secret. Next workflow run uses the new value. + +Rotations on `ZCP_API_KEY` and `ZEROPS_TOKEN` are independent unless you reused the same Zerops token for both. + +## Confirmation gates for destructive actions + +A valid token doesn't unlock everything. Two operations carry an explicit confirmation gate because the loss isn't reversible from inside the conversation: + +| Operation | Gate | +|---|---| +| **Service deletion** — removes the service entirely; container, deployed code, env vars, and (for managed services) data go with it. | The MCP tool contract requires explicit user approval in the same conversation, by service name. Supported clients (Claude Code) enforce this. ZCP itself hard-blocks one case: deleting the hosted ZCP service it's running on (`SELF_SERVICE_BLOCKED`). | +| **Wholesale service replacement** (import override) on a service with prior failed deploy history | First call is refused with a structured payload naming the operation and target hostnames. The second call must echo that payload back as confirmation. | + +Everything else the agent runs — deploys, env changes, lifecycle actions, restarts, scaling — runs without pausing for approval. Most are reversible by another call. A few are operationally destructive even though they aren't gated (deleting an env value, rotating a secret used by running services); you stay in the loop on those through the verify pass and the audit trail, not a pre-call gate. + +Approval is an explicit yes paired with the service name in the same turn-window. Approval from a previous chat doesn't carry forward — a new conversation is a new approval window. + +### Diagnose before destruct + +When a service is in trouble — non-running state, failed last deploy, or both — ZCP enforces one rule across destructive paths: read the platform's account of why before destroying anything. The first call against a service with failure history is refused; the agent reads the failure classification, events, and logs, then either fixes the cause or comes back with a confirmation that includes a summary of what it read. + +The rule prevents two failure modes: + +- **Delete-and-retry loops** — the agent decides the cleanest fix is to delete and re-import, masking the cause every time. +- **Override-as-reset** — `zerops_import override=true` becomes a "make it like new" button that erases the failure context the next session needs. + +Recovery state isn't broken state. A service that came up `READY_TO_DEPLOY` and is waiting for code isn't in trouble — the gate fires on platform-recorded failure, not on idle. + +For the broader threat model and what ZCP refuses outright, see [Trust model](/zcp/security/trust-model). + +## Gotchas + +- **Multi-project tokens are refused at startup, not at first deploy.** A full-access token won't let ZCP boot at all. Generate a per-project token before connecting. +- **`zcp init` overwrites `.mcp.json`.** If you added `ZCP_API_KEY` to the file and re-run `zcp init`, the regenerated template ships without your token. Re-add it before launching the agent. +- **`GIT_TOKEN` is not `ZCP_API_KEY`.** `GIT_TOKEN` authorizes git push (hosted, when ZCP performs the push); `ZCP_API_KEY` authorizes ZCP against Zerops. Mixing them grants too much or fails authentication. +- **`ZEROPS_TOKEN` in GitHub Actions is a Zerops API token, not a GitHub PAT.** If a deploy from Actions fails on permissions and you reach for a GitHub PAT, you've rotated the wrong credential. +- **Rotation is picked up on next ZCP startup, not mid-session.** Restart the agent after rotating; the live session still holds the old value in process memory. +- **Confirmation must be in the same conversation.** Approval from a previous chat doesn't carry forward. +- **A successful confirmation is still a destructive call.** Once you acknowledge replacement, the override runs. Roll-back isn't automatic — recover lost data through [Backup](/features/backup), review what changed through [Auditing agent work](/zcp/security/auditing-agent-work). + +## Related + +- [Trust model](/zcp/security/trust-model) — the project boundary this page enforces in practice. +- [Roles & Permissions](/features/rbac) — how Zerops scopes access tokens at the platform layer. +- [Use ZCP locally](/zcp/setup/local-agent-bridge) — where the local token gets configured. +- [Choose how finished work ships](/zcp/workflows/delivery-handoff) — where `GIT_TOKEN` and `ZEROPS_TOKEN` come into play. +- [GitHub integration](/references/github-integration) — full reference for the dashboard and Actions paths that consume `ZEROPS_TOKEN`. diff --git a/apps/docs/content/zcp/security/trust-model.mdx b/apps/docs/content/zcp/security/trust-model.mdx new file mode 100644 index 00000000..ea071aa9 --- /dev/null +++ b/apps/docs/content/zcp/security/trust-model.mdx @@ -0,0 +1,97 @@ +--- +title: "Trust model" +description: "ZCP gives a coding agent project-scoped power inside one Zerops project. The hosted and local paths set different blast radii." +--- + +ZCP gives a coding agent project-scoped power inside one Zerops project. Same control plane, two ways to run it — and the blast radius is different. That difference is the headline of this page. + +## Hosted workspace or local agent bridge — two different blast radii + +```mermaid +flowchart LR + subgraph hosted["Hosted workspace — safe by design"] + direction TB + container["zcp@1 container
(MCP + agent)"] + proj1["Project services
(via private network)"] + container --> proj1 + end + hosted x--x other1["Other Zerops projects"] + hosted x--x laptop1["Your laptop /
home directory"] + + subgraph local["Local agent bridge — supervise the client"] + direction TB + machine["Your laptop"] + machine --> agentL["Agent client
(your editor, your user)"] + machine --> binL["zcp binary
(MCP, project-scoped)"] + agentL --> files["Files, shell,
other apps"] + binL --> proj2["Project services
(via VPN)"] + end + local x--x other2["Other Zerops projects"] +``` + +| | Hosted workspace | Local agent bridge | +|---|---|---| +| Where the agent runs | A `zcp@1` service container in your project | Your laptop | +| What it can touch | The container, project services, project token, and (when requested) runtime filesystems via SSHFS mounts | The same project surface, **plus whatever the agent client can reach on your laptop** | +| Network reach | The project's private network | Project services over VPN; everything else your laptop can already reach | +| Safety profile | **Safe by design** — structural isolation; no path to your laptop, home directory, or other projects | **Supervise the agent client** — ZCP stays project-scoped, but the agent process inherits your user | + +Either path is the right choice in context. The local bridge fits when you want to keep your editor, dotfiles, and toolchain — recognize the security model and set the agent client's permissions accordingly. + +## Project is the boundary + +One ZCP process works against one Zerops project. The boundary starts at authentication: at startup, ZCP resolves the token to a project before it exposes operations. A token with access to no projects is refused; a token with access to multiple projects is refused. The intended shape is a Zerops token scoped to exactly one project. + +Zerops [RBAC](/features/rbac) remains the authority. If the token can deploy, manage env vars, read logs, or operate services in that project, ZCP exposes those operations. If Zerops rejects the token for an operation, ZCP can't bypass it. + +| Question | Boundary | +|---|---| +| What project can ZCP see? | The one project resolved from the token at startup. | +| What can the agent change? | Anything the token can change inside that project: runtime services, managed services, env vars, deploys, logs, lifecycle, scaling, public access. | +| What's outside reach? | Other projects, organization-wide settings, any resource Zerops RBAC doesn't grant. | +| Network scope? | The project's private network. See [Public access and private networking](/features/access). | + +Project-scoped doesn't mean read-only. A full project token is still powerful inside that project — treat it like a project-level operations credential. Use it on dev/staging projects, rotate it when needed, keep it out of repositories. + +## Hosted workspace specifics + +The hosted workspace is the safe-by-design path. A few specifics: + +- **`zcp` service ≠ runtime service.** The ZCP service is the control surface; runtime services are where your app code runs. Deploys target a runtime service, not `zcp`. +- **Two relaxed isolation flags make agent work possible.** The hosted `zcp@1` service ships with `envIsolation: none` (so it can read env from other services in the project — what lets the agent connect to your databases without you copying credentials around) and `sshIsolation: vpn service@zcp` (so it can SSH into the services you select — what makes SSHFS-mounted dev work). Both are scoped to ZCP itself; other services keep their normal isolation defaults. +- **Filesystem reach is narrower than network reach.** The agent reaches every project service over the private network, but only sees runtime files through SSHFS mounts when requested. `/var/www` is the SSHFS mount root, not an app repository. +- **A terminal in the hosted workspace has the same project-scoped power as the agent.** Convenient, and the reason the preview guidance keeps production in a separate project. +- **The editor reaches the workspace via a Zerops public subdomain.** Authentication, access logging, custom domains, and revocation follow standard [public access](/features/access) rules — the workspace isn't a special case. For VPN-only access instead, flip the per-service setting. + +## Local agent bridge specifics + +The local bridge is the supervise-the-client path. A few specifics: + +- **Per-project files are isolated.** Each project directory has its own `.mcp.json` and `.zcp/state/`. Switching projects means switching directories; ZCP doesn't merge local state. +- **VPN setup is outside ZCP's authority.** Bringing up the Zerops VPN requires admin or root approval on your laptop. ZCP tells you the `zcli vpn up` command; it doesn't start the VPN. +- **`.env` is generated, not synced.** ZCP writes a snapshot when you ask for it. Regenerate after project env changes. +- **The local bridge doesn't protect your checkout from the agent client.** ZCP's boundary is the Zerops side; your client's permissions decide what happens on your laptop. + +Full setup: [Use ZCP locally](/zcp/setup/local-agent-bridge). + +## What ZCP refuses by design + +ZCP uses hard refusals for boundaries that should never be inferred, and confirmation gates for actions that are destructive but sometimes necessary. + +| Behavior | When it triggers | Result | +|---|---|---| +| Refuse a token with no project access | Startup can't resolve any Zerops project from the token | ZCP doesn't start. Use a token with access to one project. | +| Refuse a multi-project token | Startup sees more than one accessible project | ZCP doesn't choose for the agent. Generate a project-scoped token or scope `zcli` to one project. | +| Refuse hosted self-deletion (`SELF_SERVICE_BLOCKED`) | A service-deletion call targets the service ZCP is running on | Blocked. Remove the workspace via the Zerops UI or `zcli` if you really intend to. | +| Require named approval for deletion | The agent wants to delete a service | Explicit approval in the current conversation, by service name. The agent doesn't delete proactively. | +| Gate destructive service replacement | An import override would replace services with prior failed deploy history | First call shows what would be destroyed and refuses; second call must acknowledge the same payload before the import proceeds. | + +The import gate isn't a permanent blocker — replacing a failed service can be the right recovery path. The gate makes the loss explicit first: which service stacks are being replaced, which targets are acknowledged, which operation is being confirmed. + +Detailed confirmation flow and the diagnose-first rule: [Tokens and credentials → Confirmation gates](/zcp/security/tokens-and-project-access#confirmation-gates-for-destructive-actions). + +## Next steps + +- [Tokens and credentials](/zcp/security/tokens-and-project-access) — token scope, storage, rotation, and confirmation gates. +- [Auditing agent work](/zcp/security/auditing-agent-work) — review what the agent changed and deployed. +- [Production boundary](/zcp/security/production-policy) — keep production outside the public-preview loop. diff --git a/apps/docs/content/zcp/setup/choose-workspace.mdx b/apps/docs/content/zcp/setup/choose-workspace.mdx new file mode 100644 index 00000000..25d6110e --- /dev/null +++ b/apps/docs/content/zcp/setup/choose-workspace.mdx @@ -0,0 +1,102 @@ +--- +title: "Choose your workspace" +description: "Pick whether ZCP MCP runs hosted inside the Zerops project (with optional Cloud IDE and bundled agent), or as a binary on your laptop." +--- + +Pick whether ZCP MCP runs hosted inside the Zerops project — wrapped in a `zcp@1` service that can also bundle a browser-based VS Code and a coding-agent CLI — or as a binary on your laptop while you're connected to the project VPN. Either path connects the same agent to the same project. What differs is the safety profile, what's bundled around the MCP, and which filesystem the agent sees. + +:::info Both modes are public preview; local is the more WIP one +Hosted is the recommended starting point. The local agent bridge is real and supported, but the binary, `.mcp.json`, and `zcp init` artifacts are still settling — expect setup churn between releases. +::: + +## The short rule + +- **First run, nothing installed locally** → **hosted workspace**. Safe by design: the workspace runs in a project container with no path to your laptop or your other projects. +- **Already work in your own editor on your own machine, want the agent to deploy from there** → **local agent bridge**. ZCP itself is project-scoped, but the agent process inherits your user — you supervise the client's permissions on your laptop. + +Switching later is straightforward. The MCP operations are the same in both modes; what's bundled around them differs by environment. Full security framing: [Trust model](/zcp/security/trust-model). + +## Hosted workspace + +A Zerops service of type `zcp@1` runs the ZCP MCP inside your project. The same service optionally bundles a browser-hosted VS Code (the Cloud IDE), Claude Code preinstalled and pre-authenticated, and a curated dev toolchain. The editor is one click from the Zerops dashboard. + +Open the workspace and a terminal already has Claude Code running, scoped to this project, talking to the project's MCP. You give the agent intent in plain language; the agent calls MCP operations to discover services, edit files, deploy, and verify against the live URL. + +**Pick this when** + +- You want the fastest first run (it's what the [Quickstart](/zcp/quickstart) uses). +- You're evaluating ZCP and don't want to install anything yet. +- You want the agent to have direct filesystem access to runtime services in the project. +- You work from multiple machines and want the same workspace to follow you. + +**What it gets you on top of the MCP** + +- SSHFS-mounted access to runtime services so the agent can read and edit files where they actually run. +- A long-lived dev process inside another project container when the workflow needs one. +- Project-local network reach without a VPN. +- Clean separation between your laptop and the work — the project owns the editor, the credentials, and the running code. + +**Cost** — one small `zcp` service in the project, billed like any other Zerops service. The editor lives in your browser instead of your local IDE. + +Setup: [Provision a hosted workspace](/zcp/setup/hosted-workspace). + +## Local agent bridge + +Install the `zcp` binary on your laptop and run `zcp init` in your project directory. The agent runs in your editor; ZCP MCP runs as a process on your machine and talks to the Zerops API on your behalf. Your laptop is the development environment — your normal local setup with ZCP wired in. + +The agent edits code in your working directory, runs your usual local dev server, deploys to Zerops through the MCP, and verifies the deployed result. Managed services (database, cache, storage) live in the project; your laptop reaches them over the Zerops VPN with credentials ZCP writes into a local `.env`. + +**Pick this when** + +- You already have an established local setup (your editor, shell, toolchain) and want to keep it. +- The team works from local machines, each developer's editor driving its own MCP. +- You want hot-reload locally while the agent deploys to a Zerops stage runtime. + +**What it gets you on top of the MCP** + +- A locally generated `.env` with real credentials so your local app can connect to managed services. +- One-command deploy from your working directory to a linked Zerops runtime. +- Native editor speed, your existing keybindings, plugins, and workflow. + +**Cost** — [zCLI](/references/cli) installed on your machine, the Zerops [VPN](/references/networking/vpn) up to reach managed services by hostname, and a `.env` file with real secrets in your working directory (gitignore it immediately). Your local dev server stays your tool's job; ZCP doesn't start, stop, or watch it, and doesn't mount remote filesystems on your laptop. + +Setup: [Use ZCP locally](/zcp/setup/local-agent-bridge). For the broader local-dev landscape (VPN, Cloud IDE, native IDE over SSH), see [Local & Remote Development](/features/local-remote-development). + +## Hosted vs local at a glance + +| | Hosted workspace | Local agent bridge | +|---|---|---| +| Safety profile | **Safe by design** — isolated project container | **Supervise the agent client** — agent runs as your user | +| Where the MCP runs | Inside the project, in a `zcp@1` service | On your laptop, as a binary | +| Where the agent runs | Browser-hosted code editor | Your local editor | +| Filesystem the agent sees | Project services via SSHFS mounts | Your local working directory | +| Local install | None | [zCLI](/references/cli) + Zerops [VPN](/references/networking/vpn) | +| Reaches managed services via | Project-private network | VPN to the project | +| Best for first run | Yes | After you've used ZCP once | +| Maturity | Public preview | Public preview, more WIP | + +The MCP operations are the same on both sides. Hosted bundles SSHFS, SSH/container deploys, server-side batch deploys, a dev-server runner, and a Cloud IDE; local adds deploys from your working directory and `.env` generation for local apps. + +## Workspace shapes + +Once hosted-vs-local is decided, the agent reads the project state and identifies a **workspace shape** — its name for the combination of *where files live* and *whether there's a separate stage*. You almost never pick this directly; the agent tells you which it found. + +| Shape | When | Typical names | +|---|---|---| +| `standard` — dev runtime plus an explicit stage runtime | You want a separate target the agent verifies before promoting | `appdev` + `appstage` | +| `dev` — one mutable dev runtime, no stage yet | A single iteration target without splitting dev from stage | `appdev` | +| `simple` — one runtime, no dev/stage split | Small apps, demos, static sites | `app` | +| `local-stage` — local checkout linked to one Zerops runtime as the stage target | Local hot reload + deploys to a Zerops stage runtime | local checkout + linked runtime | +| `local-only` — local checkout, no Zerops runtime linked yet | Project is local-only or has only managed services | local checkout | + +Stage is a deploy target for dev and review, not production. Production lives in a separate Zerops project — see [Production boundary](/zcp/security/production-policy). + +The stage hostname is explicit. The agent doesn't invent it from the dev hostname — `appstage` next to `appdev` is one convention, not a rule. The confirmed project state supplies the stage hostname; the agent uses what's there. + +Workspace shape (`standard`, `dev`, `simple`, `local-stage`, `local-only`) is ZCP's name for project topology. Zerops service `mode` (`HA`, `NON_HA`) is the scaling shape of an individual managed service — see [Scaling](/features/scaling). The two are independent. + +## Next steps + +- [Provision a hosted workspace](/zcp/setup/hosted-workspace) — recommended for the first run. +- [Use ZCP locally](/zcp/setup/local-agent-bridge) — local editor, VPN, `.env` generation. +- [Build and verify an app](/zcp/workflows/build-and-verify-app) — the deploy and verify loop in either workspace. diff --git a/apps/docs/content/zcp/setup/hosted-workspace.mdx b/apps/docs/content/zcp/setup/hosted-workspace.mdx new file mode 100644 index 00000000..1f239d9b --- /dev/null +++ b/apps/docs/content/zcp/setup/hosted-workspace.mdx @@ -0,0 +1,95 @@ +--- +title: "Provision a hosted workspace" +description: "Add a Zerops Control Plane service to your project. The service runs ZCP MCP, hosts a browser-based VS Code, and ships Claude Code preconfigured." +--- + +Add a Zerops service of type `zcp@1` to your project. The service runs the ZCP MCP inside the project, hosts a browser-based VS Code (the Cloud IDE), and ships Claude Code preconfigured in the terminal. By the end of this page you have a project with the hosted workspace running, an open browser editor, and Claude Code waiting for the first instruction. + +This page assumes you've read [Choose your workspace](/zcp/setup/choose-workspace). For a fully walked-through example with a real prompt and a deployed app at the end, jump to the [Quickstart](/zcp/quickstart). + +For the broader local-dev landscape (humans, not just agents), see [Local & Remote Development](/features/local-remote-development). + +## Two paths to the same workspace + +Zerops provisions the workspace for you — no manual install, no YAML to paste. Pick the path that matches what you're starting from: + +- **Path A — Start from a recipe.** New project, known stack (e.g. a Laravel app with managed Postgres + Valkey + storage). Open a recipe in the catalog, pick the **AI Agent** environment, deploy. Zerops creates the project with the recipe's services plus the `zcp` service. +- **Path B — Add the service to a custom or existing project.** Blank project, or one already running. Toggle **Add Zerops Control Plane (ZCP) service** during creation, or add a `zcp` service to an existing project the same way you add any other Zerops service. + +Both paths produce the same result: a `zcp@1` service inside the project with `ZCP_API_KEY` injected automatically and scoped to this project. You don't configure the token by hand. + +## Path A — Recipe + AI Agent environment + +Use this when you want a working stack quickly and the recipe matches the runtime shape you need. + +1. Open the [Zerops recipes catalog](https://app.zerops.io/recipes) and pick a recipe — for example the [Laravel showcase](https://app.zerops.io/recipes/laravel-showcase?environment=ai-agent). +2. On the recipe page, select the **AI Agent** environment. The deploy button updates to include `-agent` (e.g. **Deploy laravel-showcase-agent**). +3. Click deploy. Zerops provisions the project. + +The recipe ships its full service set; the **AI Agent** environment is what adds the `zcp` service alongside. + +If you pick a non-AI environment (Remote CDE, Local, Stage, Small Production, Highly-available Production) the recipe still deploys, but no `zcp` service is added and no agent workspace exists. Add ZCP afterward via Path B. + +## Path B — Toggle the ZCP service + +Use this for a custom project with no recipe, or when you already have a Zerops project running. + +For a **new project**: + +1. Open [Add new project](https://app.zerops.io/dashboard/project-add). +2. Enter a project name, region, and (optionally) tags. +3. Toggle **Add Zerops Control Plane (ZCP) service** on (below Project Access). +4. The **ZCP CONFIGURATION** panel appears with two cards: **Coding Agent** (Claude Code with Subscription Login) and **Cloud IDE** (VS Code, Public Access). Defaults are sensible — click **Configure** to adjust, otherwise leave and submit. +5. Submit. + +For an **existing project**, add a `zcp` service from the project dashboard the same way you add any other service. End state is identical to the new-project path: a `zcp@1` service alongside your runtime services with `ZCP_API_KEY` injected automatically. + +## Open the workspace + +The `zcp` service hosts a browser-based code editor (code-server) with Claude Code preconfigured in the terminal. + +1. Open the project in the [Zerops dashboard](https://app.zerops.io/). +2. Open the `zcp` service. +3. From the service page, open its public subdomain — code-server launches in a new tab. The public subdomain is enabled automatically; see [Public access](/features/access) for the routing model. + +After the first-run bootstrap, runtime services are SSHFS-mounted into the workspace filesystem with one folder per runtime in the file explorer. Editing a file under a runtime mount changes the file inside that runtime service — no upload step. Filesystem access stays inside the project network, so your laptop doesn't need a VPN for the hosted workspace. + +If you added ZCP to an existing project with services not yet adopted, the agent runs an adoption pass before any code work — see [Create or adopt services](/zcp/workflows/create-or-adopt-services). + +The workspace is persistent. Close the browser tab and reopen the workspace link later; editor state, open files, and Claude Code's running session survive across reconnects as long as the `zcp` service is running. + +## Verify the connection + +When the editor opens, the terminal already has Claude Code running, scoped to this project, with `ZCP_API_KEY` in its environment. + +Ask Claude Code to list the project services: + +```text +List the services in this project and tell me which are runtimes versus managed dependencies. +``` + +A working setup answers with the runtime services (e.g. `appdev`, `appstage`) and managed services (e.g. `db`), and identifies the workspace shape — `standard`, `dev`, or `simple` for the hosted workspace. (Local-mode shapes `local-stage` and `local-only` are listed alongside in the [full taxonomy](/zcp/setup/choose-workspace#workspace-shapes).) + +If Claude Code can't reach ZCP, the service may still be starting. Wait for it to reach running state in the dashboard and reload the workspace. + +You're ready to give the agent its first instruction. Try the [Quickstart](/zcp/quickstart) for an end-to-end example, or read [How ZCP works](/zcp/concept/how-it-works) for the model first. + +## Hack on the workspace + +The hosted workspace is a normal Zerops service running the `zcp@1` Ubuntu base image. The configuration generated when you toggle Coding Agent and Cloud IDE is a starting point — read the generated `zerops.yml`, edit it, redeploy. + +Common patterns: + +- **Add tools per workspace.** SSH into the `zcp` service and `apt install` whatever else your team needs. To make it persistent, move the install commands into `run.initCommands` in `zerops.yml`. +- **Pre-warm caches or run side processes.** Add additional `run.startCommands`, or define service-level processes that run alongside the agent and Cloud IDE. +- **Inject team config into every workspace.** Put your developer-onboarding script (dotfiles, shell config, framework setup) into `run.initCommands`. It applies to every workspace and every agent session automatically. +- **Fork the base image.** When changes outgrow `initCommands`, build your own image off `zcp@1` and ship a hardened or extended version with team-standard tools baked in. + +Run additional processes alongside the bundled agent and Cloud IDE — a pre-warmed cache, a custom inspector, your own MCP server. The service is yours; ZCP is one of the things running inside it, not the whole product. + +## Gotchas + +- **The recipe environment selector controls whether ZCP is added.** If the deploy button on a recipe page is `Deploy ` (no `-agent` suffix), you've got a non-AI environment selected. Reselect the **AI Agent** environment or use Path B afterward. +- **The `zcp` service is not the application.** It's where the agent operates *from*; your runtime services (`appdev`, `appstage`, `app`) are where code is deployed. Ask the agent to deploy a runtime service, not `zcp`. +- **Don't set `ZCP_API_KEY` by hand in the hosted workspace.** It's platform-injected. Manual override risks breaking the agent's access. (The local bridge is the only path where you manage the token yourself.) +- **First load takes a minute.** Provisioning includes pulling the workspace image, starting code-server, and registering Claude Code. Until the service reaches running state, the workspace URL may return an empty page or error. diff --git a/apps/docs/content/zcp/setup/local-agent-bridge.mdx b/apps/docs/content/zcp/setup/local-agent-bridge.mdx new file mode 100644 index 00000000..9104b394 --- /dev/null +++ b/apps/docs/content/zcp/setup/local-agent-bridge.mdx @@ -0,0 +1,123 @@ +--- +title: "Use ZCP locally" +description: "Run the zcp binary on your laptop and connect your local editor's coding agent to a Zerops project over the project VPN." +--- + +Run the `zcp` binary on your laptop. With `zcli vpn up `, your laptop joins the project's private network — the same network your services use. The agent runs in your editor; ZCP MCP runs as a process on your machine and bridges between the two. + +:::warning Public preview — local mode is the part most likely to change +The `zcp` binary, the `.mcp.json` shape, and the artifacts written by `zcp init` are still settling. Pin local mode to development and staging projects, keep production in a separate project, and expect setup details to shift between releases. +::: + +The local agent bridge is for developers who already have a comfortable setup — your editor, your shell, your dev server — and want a coding agent that can drive Zerops without giving up your machine. If you're evaluating ZCP for the first time, the [hosted workspace](/zcp/setup/hosted-workspace) is a faster start. + +You don't need ZCP at all to develop locally against a Zerops project — `zcli vpn up` plus your editor is enough; see [Local & Remote Development](/features/local-remote-development). ZCP is what you add when you also want a coding agent operating the project from the same machine: discovering services, generating credentials, deploying, reading logs, verifying. + +:::caution Supervise the agent client +The local bridge runs the agent on your dev machine. ZCP itself stays project-scoped, but the agent process inherits your user — what your client (Claude Code, etc.) does with file edits, shell commands, and other tools follows your client's permissions, not ZCP's. Set those permissions accordingly. See [Trust model](/zcp/security/trust-model). +::: + +## Prerequisites + +- A Zerops project. Create one from the [Zerops dashboard](https://app.zerops.io/dashboard/project-add) or from a [recipe](https://app.zerops.io/recipes). +- [zCLI](/references/cli) installed on your machine and authenticated. +- A **project-scoped Zerops token** (steps below). Multi-project tokens are refused at startup. +- A **Claude Pro or Max subscription** and Claude Code installed locally. Claude Code handles login through its own flow; ZCP doesn't see your subscription credentials. + +## Get a project-scoped Zerops token + +ZCP needs a Zerops API token scoped to **one project** — multi-project and full-access tokens are refused at startup. Generate one in the Zerops dashboard, then keep the value handy for the **Add your token** step. Full walkthrough, rejection rules, and rotation: [Tokens and credentials](/zcp/security/tokens-and-project-access). + +## Install the binary + +```bash +curl -sSfL https://raw.githubusercontent.com/zeropsio/zcp/main/install.sh | sh +``` + +The script downloads the latest release for your platform into `~/.local/bin` (or `/usr/local/bin` if run as root). Verify with `zcp version`. If `~/.local/bin` isn't on your `PATH`, add it and reload your shell. + +## Initialize ZCP in your project directory + +In a terminal in your project's working directory: + +```bash +zcp init +``` + +`zcp init` writes: + +- `.mcp.json` — MCP configuration Claude Code reads to discover ZCP +- `CLAUDE.md` — agent instructions for operating ZCP in this project +- `.claude/settings.local.json` — Claude Code per-project settings +- `~/.config/zerops/aliases` plus a shell-rc sourcing line — defines the `zcl` alias for launching Claude Code + +`.zcp/state/` appears later, on the first state write for adoption, sessions, or workflow progress. Re-running `zcp init` refreshes generated files; `CLAUDE.md` preserves your edits outside the managed markers, but `.mcp.json` is rewritten from the template every time, so re-add `ZCP_API_KEY` after re-init. + +## Add your token + +`zcp init` writes a token-less `.mcp.json`. Add your project-scoped token under an `env` block (`ZCP_API_KEY`), gitignore the file, and launch Claude Code from your project root so it picks up the config. Exact JSON shape and rotation: [Tokens and credentials → Where the token lives](/zcp/security/tokens-and-project-access#where-the-token-lives--hosted-vs-local). + +Claude Code starts and lists the MCP servers it found. The `zerops` server (that's ZCP MCP) should be in the list. + +### Sanity check + +```text +Use ZCP to list the services in this project. +``` + +A working connection answers with the project's runtime and managed services and their state. If the agent says it can't reach ZCP, see [Troubleshooting](/zcp/reference/troubleshooting) — most common issues are token-scope mismatches and starting Claude Code from the wrong directory. + +## Connect the project VPN + +ZCP reaches managed services (databases, caches, storage) through the Zerops project [VPN](/references/networking/vpn). Bring it up with zCLI: + +```bash +zcli vpn up +``` + +You'll be prompted for sudo or admin — VPN setup needs root on Linux and macOS. ZCP can't start the VPN for you; this is your one manual step. + +After the VPN is up, project services resolve by hostname (`db`, `cache`, etc.) from your laptop. + +## Generate a local `.env` + +Ask Claude Code: + +```text +Use ZCP to generate a .env file for my local app. +``` + +ZCP reads `run.envVariables` for the requested service from your local `zerops.yaml`, resolves env-template references (the `${hostname_var}` syntax) from the platform, and writes a `.env` in your working directory. Cross-service references resolve recursively — for example `DATABASE_URL=postgres://${db_user}:${db_password}@${db_hostname}:${db_port}/${db_dbName}` lands as a fully-resolved connection string. + +The call needs the target service hostname, a `zerops.yaml` in your working directory, and a matching `setup:` entry with non-empty `run.envVariables`. Without those, generate-dotenv returns an error instead of guessing. + +Your local app reads the same hostnames and credentials it would see deployed, just reached over VPN. The `.env` is a snapshot — regenerate after changing project env variables. + +## Link a deploy target + +Local mode needs one Zerops runtime linked as your stage target so the agent has somewhere to deploy from your working directory. If your project has exactly one runtime, ZCP picks it up automatically. If multiple, the agent asks which to link — answer by hostname (e.g. `appstage`). + +If no runtime is linked yet (project has only managed services, or you haven't decided), the agent's default deploy path refuses with a hint. You can still develop locally against the managed services, and git-push delivery can still work — see [Choose how finished work ships](/zcp/workflows/delivery-handoff). Ask the agent to link a runtime by hostname when ready. + +Workspace shapes the agent uses here: `local-stage` once a runtime is linked, `local-only` until then. Full taxonomy: [Workspace shapes](/zcp/setup/choose-workspace#workspace-shapes). + +## What stays your tool's job + +ZCP doesn't run your local dev server. Vite, Valet, Docker Compose, your IDE's runner, whatever — the dev process stays your tool's responsibility. ZCP works alongside it. + +ZCP doesn't mount Zerops runtime filesystems on your laptop. The hosted workspace gives the agent SSHFS access; the local bridge doesn't. If you need to read runtime files from your machine, use [SSH](/references/networking/ssh) directly. + +## Gotchas + +- **VPN goes down with sleep.** Laptop sleep, network change, idle timeout — the tunnel often drops. Bring it back with `zcli vpn up ` before the agent retries. +- **Each project directory needs its own `zcp init`.** State and tokens are per-project. Switching projects means switching directories; Claude Code picks up the wrong ZCP connection if you launch from the wrong root. +- **Multi-project tokens are rejected at startup.** A token granting access to multiple projects (or no project) is refused. Generate a per-project token (steps above). +- **The `zerops` server name in `.mcp.json` is fixed.** Don't rename the key; ZCP and Claude Code both expect it. +- **The `.env` is a snapshot.** Regenerate after changing project env variables, not before. +- **Production stays out of this loop.** Local bridge is for development and staging projects. See [Production boundary](/zcp/security/production-policy). + +## Next steps + +- [Build and verify an app](/zcp/workflows/build-and-verify-app) — the deploy and verify loop with the agent. +- [Tokens and credentials](/zcp/security/tokens-and-project-access) — token scope, rotation, confirmation gates. +- [Choose your workspace](/zcp/setup/choose-workspace) — hosted-vs-local + workspace shapes. diff --git a/apps/docs/content/zcp/workflows/build-and-verify-app.mdx b/apps/docs/content/zcp/workflows/build-and-verify-app.mdx new file mode 100644 index 00000000..db238052 --- /dev/null +++ b/apps/docs/content/zcp/workflows/build-and-verify-app.mdx @@ -0,0 +1,101 @@ +--- +title: "Build and verify an app" +description: "Use ZCP's deploy and verify loop to edit code, deploy through Zerops, read logs, fix failures, and verify behavior." +--- + +Edit code, deploy through the Zerops pipeline, watch logs, fix failures from platform-classified evidence, verify the result against a real URL. The loop closes when every service in scope has a successful deploy and a passing verify. + +This workflow owns the application work — code, `zerops.yaml`, the first deploy, and every iteration after. It runs after [Create or adopt services](/zcp/workflows/create-or-adopt-services) closes, and every time you ask the agent to change something. + +Localhost isn't delivery. For an agent, a deploy is diagnostics — it surfaces problems localhost can't. The verify that follows is the proof that the requested behavior actually works. + +## Start the work + +Before any code changes, you and the agent agree on the runtime scope — which service this change targets. In a dev/stage project that's the dev runtime (`appdev` or similar). In a single-runtime project, the runtime IS the application service. In the [local agent bridge](/zcp/setup/local-agent-bridge), scope is your local checkout plus the linked stage runtime. Full taxonomy: [Workspace shapes](/zcp/setup/choose-workspace#workspace-shapes). + +Wrong path: writing app code before the runtime scope is named. If the agent jumps straight to editing without naming which service is in scope, the first deploy lands somewhere unexpected. + +Good starting prompts: + +```text +Add a /health endpoint to appdev and verify it returns 200. +``` + +```text +Fix the failing migration on appdev and re-deploy. +``` + +```text +What is the next step in this project? Read ZCP status first. +``` + +The agent reads the project state, names the scope, and starts. If it asks you to confirm scope, that's a feature — `standard` projects have two runtimes, and the agent shouldn't assume stage when you asked for dev. + +Managed dependencies (databases, caches, storage) are dependencies, not deploy targets. If the agent tries to deploy `db`, that's a wrong path. + +## Deploy + +When code is ready, the agent runs the standard Zerops [build and deploy pipeline](/guides/deployment-lifecycle). A direct deploy blocks until the build completes and the runtime is ready — the agent waits with you, not for you. Git-push delivery is asynchronous; the call returns after pushing and the agent polls events until the runtime goes ACTIVE. See [Choose how finished work ships](/zcp/workflows/delivery-handoff). + +- **First deploy is always direct.** Delivery handoff applies to subsequent changes, not the first. +- **Build is separate from runtime.** Build runs in a temporary build container; the runtime is created with the new appVersion. The agent reads logs from each phase distinctly. +- **Build logs and runtime logs are different streams.** A build failure shows in build-container logs; a runtime crash shows in runtime-container logs. The agent picks the right stream for the failure category — pasting runtime logs for a build failure produces a wrong diagnosis. +- **Hosted multi-service deploys run in parallel.** When several services ship together from a hosted workspace, ZCP runs them concurrently via deploy-batch. From the local bridge, deploys run serially per service. In a dev+stage workflow, the typical order is dev first, verify, then deploy stage from the verified dev build. + +Deploy success and verify success aren't the same. A successful deploy means the build finished and the runtime started; whether the requested behavior works is a separate check. + +## Read logs and events + +When something looks wrong, ZCP gives the agent direct access to platform evidence — not your retell of it. + +- The project activity timeline shows recent deploys, builds, status transitions, and process events, scoped by service hostname. +- Runtime logs come back filtered by severity, time window, and search text — the agent does not need to scroll a wall of output. +- Health checks return service status, error logs, and an HTTP probe on the public URL in one structured response. + +Each failed deploy carries a structured failure classification on top of the raw logs — a category, a likely cause from the platform's pattern library, and a suggested next action. The agent reads this first; full categories and recovery paths are in [Troubleshooting](/zcp/reference/troubleshooting#start-from-a-failure-category). Classification is what turns a deploy failure from "the agent guesses why" into "the agent reads the cause and fixes the right thing." + +## Verify + +After a deploy succeeds, verification runs in two layers: + +1. **The runtime is reachable.** The platform health check answers: is the service status running? Are there error logs in the runtime container? Does an HTTP probe on the public URL return a successful status? +2. **The requested behavior works.** Reachability is necessary, not sufficient. If you asked for `/health` to return 200, the agent fetches `/health` and confirms. If you asked for the dashboard to list notes, the agent opens the URL and checks the rendered page. + +Both layers must pass before a service counts as "done". The first layer is automatic; the second is the agent applying judgment to your specific request. The runtime configuration the agent edits — start command, ports, health check, env vars — lives in [`zerops.yaml`](/zerops-yaml/specification). + +If verify fails, the agent reads the failure classification and routes to the appropriate fix — same machinery that catches deploy failures, applied at the verify step. + +## Iterate, then stop + +The deploy → log → verify loop repeats until the service is verified or the agent hits a hard blocker. Iteration isn't blind retry — there's no automatic cap, so treat the cadence below as what to expect and as your signal that something's off the rails: + +| Attempt | What the agent should do | +|---|---| +| 1–2 | Diagnose locally from logs and events; fix the targeted issue | +| 3–4 | Systematic check — `zerops.yaml`, env vars, `deployFiles`, build commands, health check definition | +| 5 | Stop and ask. Five attempts without progress means the standard strategies are exhausted. | + +The workflow auto-closes when every service in scope has a successful deploy and a passing verify. The result is concrete — a working URL (or set of URLs), plus the agent's summary of what it changed and where it verified. + +If the agent gets stuck before the loop closes: + +- **Hard blockers escalate.** Missing credentials, ambiguous scope, a `zerops.yaml` problem that needs human input — the agent stops and asks instead of redeploying without new evidence. +- **Recovery comes first.** If the agent looks confused after an interruption, ask it to read ZCP status before changing anything else. See [How ZCP works](/zcp/concept/how-it-works). + +A clear blocker is a successful end of session even when the work is incomplete, as long as it names the failure category, the in-scope runtime, what was tried, and what evidence is still missing. + +## Review and take over + +Whether the loop closed cleanly or stopped on a blocker, the platform has its own record of what happened — events, runtime logs, deploy/verify history, and (when delivery uses git-push) commits. Read it before accepting the work, before approving anything destructive, or before taking over manually. Step-by-step, with the evidence-coverage matrix and the take-over decision tables, lives in [Auditing agent work](/zcp/security/auditing-agent-work). The short version: ask for ZCP status when the chat-side picture has drifted, and read the platform's account of failures before asking the agent to delete or override anything. + +## Gotchas + +1. **Deploy success is not verify success.** A green build with a 500 on the route you care about isn't finished — the build status check and the behavior check are different gates, and the second is the one that matters to a user. +2. **In a `standard` (dev/stage) project, stage deploys do not happen automatically.** Dev work targets the dev runtime only — stage deploys must be asked for. If the agent silently changes the stage runtime, that is a wrong path. (`local-stage` projects deploy from your laptop into the linked stage runtime by design and are not affected — see [workspace shapes](/zcp/setup/choose-workspace#workspace-shapes).) +3. **Five attempts is your stop signal.** A loop that cannot make progress in five tries is a loop that needs you, not another retry. ZCP does not enforce this in the deploy/verify loop — it's the cadence you should expect and the threshold beyond which you step in. + +## Next steps + +- [Choose how finished work ships](/zcp/workflows/delivery-handoff) — direct deploy, git push, or hand off to your CI. +- [Troubleshooting](/zcp/reference/troubleshooting) — failure categories and concrete recovery moves. +- [How ZCP works](/zcp/concept/how-it-works) — the full capability surface used in this loop. diff --git a/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx b/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx new file mode 100644 index 00000000..7917e966 --- /dev/null +++ b/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx @@ -0,0 +1,80 @@ +--- +title: "Create or adopt services" +description: "Use ZCP to create new Zerops services or adopt existing ones before any application work starts." +--- + +Create new Zerops services or adopt existing ones before any application work starts. This is the first workflow because the agent needs a real runtime target before it can build or deploy. + +You don't pick a route. You describe the project shape you want; the agent reads the project state and explains the choice it made. + +## What this workflow does + +Create-or-adopt is **infrastructure only**. It answers: + +- Which runtime services will hold application code? +- Which managed services does the app depend on? +- Are the services already present, or does the agent create them via ZCP MCP? +- Are they running and visible to the MCP? + +The boundary is strict. This workflow doesn't write application code, create `zerops.yaml`, run a deploy, or verify behavior. When any of those are needed, the work moves to [Build and verify an app](/zcp/workflows/build-and-verify-app). + +The `zcp` service is the workspace and the MCP endpoint — not the application. Runtime services are where your code runs (`appdev`, `appstage`, `api`, `web`). If the agent starts treating `zcp` as the app runtime, stop and correct the target. + +## Two starting situations + +| Situation | What the agent should do | +|---|---| +| **Project already has runtime services** (including any recipe-based project) | Adopt them. Identify which are runtime services and which are managed dependencies. Don't recreate or rename services just to begin. | +| **Project is empty** (only the `zcp` service, or nothing yet) | Create the runtime and managed services. Use a Zerops-published recipe when the request matches one, or a custom plan otherwise. Explain the choice and create services before any code work. | + +If an earlier setup was interrupted, ask the agent for current ZCP status before changing anything else. ZCP rebuilds the picture from live platform state and resumes from the last known step. + +## Prompts that work + +Write the prompt in terms of the project shape you want. Name a runtime hostname only when you already know it. + +```text +Create the services for a Node.js API with PostgreSQL. Use appdev and appstage. +``` + +```text +Use the existing Laravel services and build a small notes app. +``` + +```text +Build me a small team dashboard. Pick a suitable starter and explain what you chose. +``` + +The agent answers with: which path it took (adopt vs create), the target services, and the next confirmation it needs. + +## Stop conditions — when create-or-adopt is done + +The agent should report this workflow complete when: + +- Runtime services are identified and separated from managed dependencies. +- New services, if any, were created in Zerops; existing services, if any, were adopted instead of recreated. +- Managed services have exposed env-var keys for the runtimes to reference later. +- No application code was written. +- No `zerops.yaml` was authored for the app. +- No deploy ran. + +When the next task involves files, framework setup, `zerops.yaml`, deploys, logs, or behavior checks, continue to [Build and verify an app](/zcp/workflows/build-and-verify-app). + +## Common failures + +| Symptom | What it usually means | What to do | +|---|---|---| +| The agent wants to deploy `zcp` | It confused the workspace service with the app runtime | Stop and name the real runtime service. | +| The agent treats existing services as if they need creating | It's taking the create path on a project that should adopt | Ask it to inspect the project first and adopt what's already there. | +| The agent writes application files during this workflow | It crossed into application work too early | Stop and finish service setup, or move to [Build and verify an app](/zcp/workflows/build-and-verify-app). | +| The agent creates a service with a hostname that already exists | The plan didn't account for current project state | Ask it to rediscover services and revise the plan. | +| A service-creation step fails | The infrastructure request didn't complete cleanly | Treat as a hard stop. The agent should explain the failure and ask for a decision, not retry automatically. | +| The agent asks you to describe every service manually | It hasn't used live project discovery yet | Ask it to inspect the project first and explain what it found. | + +Create-or-adopt failures don't have retry magic. Provisioning infrastructure twice without understanding the first failure creates hostname conflicts or partial service state. + +## Next steps + +- [Build and verify an app](/zcp/workflows/build-and-verify-app) — write application code, create `zerops.yaml`, deploy, and verify behavior. +- [Choose how finished work ships](/zcp/workflows/delivery-handoff) — decide what happens after the app works. +- [Choose your workspace](/zcp/setup/choose-workspace) — workspace shapes and the names you'll see after services are created or adopted. diff --git a/apps/docs/content/zcp/workflows/delivery-handoff.mdx b/apps/docs/content/zcp/workflows/delivery-handoff.mdx new file mode 100644 index 00000000..84c1c9ab --- /dev/null +++ b/apps/docs/content/zcp/workflows/delivery-handoff.mdx @@ -0,0 +1,85 @@ +--- +title: "Choose how finished work ships" +description: "Choose whether ZCP closes work by deploying directly, pushing to git, or handing off to your CI." +--- + +Choose whether ZCP closes work by deploying directly, pushing to git, or handing off to your CI. Picks up after [Build and verify an app](/zcp/workflows/build-and-verify-app) — the first deploy worked, verification passed, the app does what you asked. Now decide how the next change gets out the door. + +If you don't have a CI pipeline yet, the third answer isn't for you — pick "the agent keeps deploying" and revisit when your team needs git-tracked releases. + +ZCP is for dev/staging. Production handoff goes through [Package a running service](/zcp/workflows/package-running-service); production then lives in its own Zerops project deployed by your CI or release pipeline. + +## Three answers + +- **The agent keeps deploying directly.** Future changes ship the same way the first deploy did — straight from ZCP through the Zerops [build and deploy pipeline](/guides/deployment-lifecycle). Right for solo work and fast iteration. +- **Push to git, then build from there.** Future changes get committed and pushed to a configured remote. Either Zerops or your existing CI takes the push and runs the build. Right when the team's source of truth is git and you want reviewable commits. +- **Hand off to your CI or a human.** ZCP records every deploy and verify the agent runs at your request, but does not initiate further deploys. Right when an external pipeline or release process owns delivery from here. + +You ask for an outcome ("set up git-push delivery for appdev", "my CI takes over from here"); the agent picks the right operations. Git-push capability and what fires after a push are independent — configured git-push can coexist with the agent-keeps-deploying answer for emergencies. + +The first deploy always uses the direct path, regardless of which answer you pick later — there's no deploy yet for the choice to apply to. + +```text +Set up git-push delivery for appdev. Push to git@github.com:my-org/notes-app.git. +``` + +```text +Keep ZCP deploying appdev directly for now. +``` + +```text +ZCP records changes for appdev, but my CI takes over from here. +``` + +## The agent keeps deploying + +The deploys ZCP already ran during iteration are how the next change ships too. No further configuration; a verified session closes on the deploys that already landed. + +Pick this when the project is small or solo, when ZCP's deploys are the canonical way the app updates, and when you don't need a git-tracked history of deploys. + +## Push to git, then build from there + +Future changes get committed and pushed to a configured git remote. Whatever sits behind that remote — a Zerops-managed build or your own CI — produces the next deploy. + +This requires committed code. In the [local agent bridge](/zcp/setup/local-agent-bridge), ZCP doesn't auto-init git in your working directory — you `git init` and make your first commit. In the [hosted workspace](/zcp/setup/hosted-workspace), ZCP initializes `/var/www/.git/` for each managed runtime at bootstrap with a deploy identity, so the repository is already there; what's missing is a commit covering the work you want to ship. Git-push delivery needs at least one commit, a remote URL ZCP can push to, and credentials that authorize the push. + +When you ask the agent to set up git-push delivery, it provisions the remote URL and credentials so future pushes work without per-call configuration. If setup fails — repository unreachable, credential missing, working directory has no commits — the agent reports the specific reason. + +Push credentials differ by workspace: hosted reuses or asks for a `GIT_TOKEN` (typically a fine-grained GitHub or GitLab access token); local defers to your existing git credential helper (SSH keys, macOS keychain, `gh auth login`). Full credential reference: [Tokens and credentials → Git credentials](/zcp/security/tokens-and-project-access#git-credentials--separate-from-zcp_api_key). + +### What fires after a push lands + +- **Nothing tracked by ZCP.** Whatever your repository already has — a non-Zerops CI, a deploy hook elsewhere — keeps running. ZCP records the deploys you ask it to verify and stays out of the way. +- **The Zerops dashboard build integration.** The Zerops dashboard pulls the repository and runs the [build and deploy pipeline](/guides/deployment-lifecycle) — see [GitHub integration via the dashboard](/references/github-integration#integration-via-zerops-gui). +- **A GitHub Actions workflow you check in.** Your repository's `.github/workflows/zerops.yml` checks out the code and pushes it to Zerops via `zcli` — see [GitHub Actions](/references/github-integration#github-workflow-integration). + +The GitHub Actions option needs a Zerops API token (not a GitHub token) stored as a GitHub Actions secret named `ZEROPS_TOKEN`. The agent prepares the right `gh secret set` command for your environment; you can reuse the project-scoped token already behind `ZCP_API_KEY` for one rotation surface. Token shapes and rotation: [Tokens and credentials](/zcp/security/tokens-and-project-access). + +## Hand off to your CI or a human + +ZCP keeps recording every deploy and verify the agent runs at your request, but doesn't initiate further deploys on its own and doesn't auto-close the session. + +Pick this when an external CI/CD pipeline owns delivery, a human takes over for a release step that doesn't belong inside the agent loop, or the next change is ambiguous enough that explicit close calls beat automation. + +In this mode, ending the session is a deliberate step — the agent doesn't auto-close even after a clean deploy and verify. + +## Change later + +None of these choices are sticky. The handoff style, the git-push capability, and any build integration are read fresh on every handoff. Switch any one of them at any time: + +- Move from direct deploy to git-push once the project is large enough to want commits. +- Add a Zerops dashboard or GitHub Actions integration after git-push is already in use. +- Drop back to manual hand-off for a release where you want full control. + +## Gotchas + +1. **First deploy is always direct, regardless of the choice.** Setting up git-push delivery early does not redirect the first deploy to git. The choice governs subsequent changes, not the deploy that closed the previous workflow. +2. **GitHub Actions secret is a Zerops API token, not a GitHub token.** The workflow runs `zcli` against Zerops; it needs a Zerops API token in `ZEROPS_TOKEN`. If a deploy from Actions fails on permissions and you reflexively rotate your GitHub PAT, you have rotated the wrong credential. +3. **Build integration set to "none tracked" does not mean no build will fire.** It means ZCP did not set one up. Whatever your repository already has — a non-Zerops CI, a deploy hook elsewhere — keeps running. + +## Next steps + +- [Build and verify an app](/zcp/workflows/build-and-verify-app) — the deploy and verify loop the handoff picks up from. +- [Package a running service](/zcp/workflows/package-running-service) — turn a running service into reusable import files when the work is worth re-using. +- [GitHub integration](/references/github-integration) — full reference for the dashboard and Actions paths. +- [CI/CD with Zerops](/guides/ci-cd) — broader context on running pipelines around the Zerops build. diff --git a/apps/docs/content/zcp/workflows/package-running-service.mdx b/apps/docs/content/zcp/workflows/package-running-service.mdx new file mode 100644 index 00000000..47648591 --- /dev/null +++ b/apps/docs/content/zcp/workflows/package-running-service.mdx @@ -0,0 +1,141 @@ +--- +title: "Package a running service" +description: "Use ZCP to turn a running Zerops service into a re-importable bundle." +--- + +Turn a running Zerops service into a re-importable bundle. ZCP is for dev/staging; this workflow is the supported path for production handoff — the bundle's `buildFromGit:` URL points at the same source repo, so re-importing creates a fresh project that builds itself from git. + +Packaging takes a deployed runtime and produces two YAML files: a project [import file](/references/import) describing the services to provision, and a [`zerops.yaml`](/zerops-yaml/specification) describing how to build and run the runtime. Together they let you (or someone else) re-import the same shape into a fresh Zerops project. + +## When to package + +Packaging is the right tool when: + +- You have a running runtime (and managed dependencies) you want to reproduce in another Zerops project. +- You want a re-importable snapshot you can paste into a fresh project later, on demand. +- You want the new project to build from the same git repo, not from a copy of the code in YAML. + +Packaging is **not** the right tool when: + +- You want to deploy code right now. For that, ask the agent to deploy the runtime directly. +- You want to migrate production. Keep production in its own Zerops project and operate it through Zerops UI, the API, or CI. + +Packaging is **stateless**. The agent runs it, you answer the prompts, the bundle comes back. Stop and resume later — the agent re-runs the flow from scratch with the same answers. + +## Pick the runtime to package + +Packaging covers **one** runtime per call. If your project has a dev and a separate stage runtime, the agent asks which to capture — they may carry different env values, different start commands, or a different `setup:` block. For single-runtime projects, the choice is implied. + +Managed services (`db`, `redis`, etc.) come along automatically as dependencies — you don't pick them. + +A typical first prompt: + +```text +Use ZCP to package the appdev runtime so I can re-import this project into a fresh Zerops account next week. +``` + +The agent handles the call sequence; you only step in if it asks which half to package. + +## Classify env vars (secrets, project-scoped values, public values) + +Once the runtime and variant are picked, ZCP composes a candidate bundle and pauses on the project's env vars. Each project env needs a bucket so the generator knows what to do with it: + +| Bucket | What it means | What ends up in the bundle | +|---|---|---| +| `infrastructure` | Value comes from a managed service (for example `${db_hostname}`, `${redis_connectionString}`) | Dropped from the import file. The re-imported managed service will emit a fresh value. | +| `auto-secret` | A local signing or encryption key (Laravel `APP_KEY`, Django `SECRET_KEY`, a Node `JWT_SECRET`) | A preprocessor directive that generates a fresh secret on re-import. | +| `external-secret` | A third-party API key (Stripe, OpenAI, Mailgun, GitHub) | A `REPLACE_ME` placeholder. The new project's owner pastes the real key into the dashboard before deploying. | +| `plain-config` | Literal runtime config (`LOG_LEVEL`, `NODE_ENV`, feature flags) | The literal value, verbatim. | + +ZCP doesn't auto-bucket. Classification is the agent's job, done from the source code: if the value is read by a Stripe SDK call it's `external-secret`; if it appears as `${db_*}` from a managed service it's `infrastructure`. The packaging response carries the env *keys* but redacts the values — the agent fetches values separately when it needs them. + +You'll see the agent walk the env table, propose a bucket per key, and ask you to confirm anything it can't decide alone: + +```text +APP_KEY looks like a Laravel encryption key (auto-secret), but rotating it +will break existing encrypted columns and session cookies. Carry the +existing value forward as plain-config, or rotate? +``` + +Auto-secret rotation is destructive to any persisted state encrypted with the old key — confirm before bucketing as `auto-secret` for stateful apps. + +## What the bundle contains + +A successful run produces a **single-repo, self-contained bundle** with two files: + +```yaml +#yamlPreprocessor=on +# zerops-project-import.yaml — paste into a fresh Zerops project +project: + name: demo + envVariables: + APP_KEY: <@generateRandomString(<32>)> + LOG_LEVEL: info +services: + - hostname: appdev + type: nodejs@22 + mode: NON_HA + buildFromGit: https://github.com/example/demo.git + zeropsSetup: appdev + enableSubdomainAccess: true + - hostname: db + type: postgresql@16 + mode: NON_HA + priority: 10 + - hostname: redis + type: valkey@7.2 + mode: NON_HA + priority: 10 +``` + +```yaml +# zerops.yaml — verbatim copy from the running runtime +zerops: + - setup: appdev + build: + base: nodejs@22 + buildCommands: + - npm ci + - npm run build + deployFiles: + - dist + - package.json + - node_modules + run: + base: nodejs@22 + ports: + - port: 3000 + httpSupport: true + start: node dist/server.js + envVariables: + DATABASE_URL: postgres://${db_user}:${db_password}@${db_hostname}:${db_port}/${db_dbName} +``` + +A few things worth noticing: + +- The first line is the [yamlPreprocessor](/references/import-yaml/pre-processor) header. When the bundle carries `<@...>` directives (here `<@generateRandomString(<32>)>` for `APP_KEY`), the header is required on line 1 — without it the platform skips directive expansion at re-import and the literal string lands in the env var. Added automatically when any directive is present. +- The runtime's `buildFromGit:` URL points back at the same repo the bundle lives in. The bundle is **self-referential** — re-importing creates a project that builds itself from git. Source code lives in git, not in YAML. +- Managed dependencies (`db`, `redis`) appear as plain entries with no `buildFromGit:` so `${db_*}` and `${redis_*}` references in `zerops.yaml` resolve at re-import. See [environment variables](/guides/environment-variables). + +Once the bundle comes back, ask the agent to write the two files into your repo and commit or push them through your chosen [handoff path](/zcp/workflows/delivery-handoff). Packaging produces the bundle; it doesn't commit, push, or modify your repo. + +## What the bundle does NOT include + +| Not in the bundle | Where it lives instead | +|---|---| +| Application source code | In your git repo, referenced by `buildFromGit:` | +| Managed-service data (Postgres rows, Redis keys, object-storage objects) | Restore from [Backup](/features/backup) separately | +| External API keys (Stripe, OpenAI, etc.) | `REPLACE_ME` placeholder; new project owner pastes the real key | +| Production config | Keep production in a separate Zerops project | + +## Gotchas + +1. **Packaging does not commit or push.** It produces the bundle. Commit and push happen through your chosen [handoff path](/zcp/workflows/delivery-handoff). Treat it as a generator, not a delivery step. +2. **Managed-service data is not in the bundle.** Re-importing produces fresh empty managed services. Plan a separate restore step for any service with stateful data. +3. **External secrets land as `REPLACE_ME`.** Re-importing a bundle without filling those in deploys services that fail at startup with credential errors. + +## Next steps + +- [Choose how finished work ships](/zcp/workflows/delivery-handoff) — commit and push the bundle's two files. +- [Build and verify an app](/zcp/workflows/build-and-verify-app) — the verified runtime this packages. +- [How ZCP works](/zcp/concept/how-it-works) — why packaging stays stateless. diff --git a/apps/docs/docusaurus.config.js b/apps/docs/docusaurus.config.js index d7f27217..114d0615 100644 --- a/apps/docs/docusaurus.config.js +++ b/apps/docs/docusaurus.config.js @@ -57,6 +57,9 @@ const config = { }, ], themes: ["@docusaurus/theme-mermaid"], + markdown: { + mermaid: true, + }, themeConfig: { mermaid: { theme: { diff --git a/apps/docs/sidebars.js b/apps/docs/sidebars.js index ef910eb1..b6da0dda 100644 --- a/apps/docs/sidebars.js +++ b/apps/docs/sidebars.js @@ -140,6 +140,24 @@ module.exports = { }, className: 'homepage-sidebar-item', }, + { + type: 'doc', + id: 'features/local-remote-development', + label: 'Local & Remote Development', + customProps: { + sidebar_icon: 'computer-desktop', + }, + className: 'homepage-sidebar-item', + }, + { + type: 'doc', + id: 'features/coding-agents', + label: 'Infrastructure for Coding Agents', + customProps: { + sidebar_icon: 'sparkles', + }, + className: 'homepage-sidebar-item', + }, // { // type: 'html', // value: 'Perfectly suited for', @@ -559,6 +577,148 @@ module.exports = { }, ] }, + { + type: 'category', + label: 'Zerops Control Plane', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, + }, + items: [ + { + type: 'doc', + id: 'zcp/overview', + label: 'Overview', + customProps: { + sidebar_icon: 'sparkles', + }, + className: 'homepage-sidebar-item', + }, + { + type: 'doc', + id: 'zcp/quickstart', + label: 'Quickstart', + customProps: { + sidebar_icon: 'rocket-launch', + }, + className: 'homepage-sidebar-item', + }, + { + type: 'doc', + id: 'zcp/concept/how-it-works', + label: 'How ZCP works', + customProps: { + sidebar_icon: 'cog-six-tooth', + }, + className: 'homepage-sidebar-item', + }, + { + type: 'doc', + id: 'zcp/glossary', + label: 'Glossary', + customProps: { + sidebar_icon: 'book-open', + }, + className: 'homepage-sidebar-item', + }, + { + type: 'category', + label: 'Connect', + link: { + type: 'doc', + id: 'zcp/setup/choose-workspace', + }, + customProps: { + sidebar_icon: 'computer-desktop', + }, + className: 'homepage-sidebar-item', + items: [ + { + type: 'doc', + id: 'zcp/setup/hosted-workspace', + label: 'Provision a hosted workspace', + }, + { + type: 'doc', + id: 'zcp/setup/local-agent-bridge', + label: 'Use ZCP locally', + }, + { + type: 'doc', + id: 'zcp/security/tokens-and-project-access', + label: 'Tokens and credentials', + }, + ], + }, + { + type: 'category', + label: 'Build & ship', + link: { + type: 'doc', + id: 'zcp/workflows/build-and-verify-app', + }, + customProps: { + sidebar_icon: 'circle-stack', + }, + className: 'homepage-sidebar-item', + items: [ + { + type: 'doc', + id: 'zcp/workflows/create-or-adopt-services', + label: 'Create or adopt services', + }, + { + type: 'doc', + id: 'zcp/workflows/delivery-handoff', + label: 'Delivery handoff', + }, + { + type: 'doc', + id: 'zcp/workflows/package-running-service', + label: 'Package a running service', + }, + ], + }, + { + type: 'category', + label: 'Trust & recover', + link: { + type: 'doc', + id: 'zcp/security/trust-model', + }, + customProps: { + sidebar_icon: 'users', + }, + className: 'homepage-sidebar-item', + items: [ + { + type: 'doc', + id: 'zcp/security/production-policy', + label: 'Production boundary', + }, + { + type: 'doc', + id: 'zcp/security/auditing-agent-work', + label: 'Auditing agent work', + }, + { + type: 'doc', + id: 'zcp/reference/troubleshooting', + label: 'Troubleshooting', + }, + ], + }, + { + type: 'doc', + id: 'zcp/reference/agent-workflow', + label: 'Agent workflow contract', + customProps: { + sidebar_icon: 'command-line', + }, + className: 'homepage-sidebar-item', + }, + ], + }, { type: 'category', label: 'Networking', diff --git a/apps/docs/src/css/custom.css b/apps/docs/src/css/custom.css index da54b907..6ff6fcfb 100644 --- a/apps/docs/src/css/custom.css +++ b/apps/docs/src/css/custom.css @@ -233,3 +233,23 @@ html[data-theme='dark'] .docsearch-btn:hover { @import url('./components/toc.css'); @import url('./components/copy-page-menu.css'); @import url('./components/tooltip.css'); + +/* ZCP docs: force balanced table column widths on tablet and up. + Default markdown tables in this section render as display:block with + browser auto-layout, which collapses one column to ~17% when the other + has bold-led short text the browser treats as max-content. Equal-width + fixed layout keeps long-cell content readable. Below 768px we leave the + default responsive overflow-x behavior alone. */ +@media (min-width: 768px) { + html[class*='docs-doc-id-zcp'] .theme-doc-markdown table { + display: table; + table-layout: fixed; + width: 100%; + } + html[class*='docs-doc-id-zcp'] .theme-doc-markdown table th, + html[class*='docs-doc-id-zcp'] .theme-doc-markdown table td { + overflow-wrap: anywhere; + word-break: normal; + vertical-align: top; + } +} From 9f972fa9bd08583c135ddf4c809bc2110551de50 Mon Sep 17 00:00:00 2001 From: Ales Rechtorik Date: Thu, 7 May 2026 14:44:42 +0200 Subject: [PATCH 2/2] docs(zcp): tighten MCP / hosted-service / concept discipline + linking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Term discipline pass — Codex-verified across all ZCP pages: - Operational verbs (validates, reads, refuses, exposes, authorizes, picks up, records, enforces) now subject "the MCP" / "ZCP MCP", not bare "ZCP". - Container/wrapper references (token storage, env vars, isolation flags, SSHFS mounts, the thing you add to a project, what you SSH into) now subject "the hosted workspace" or "the `zcp` service". - "ZCP" reserved for the umbrella concept (titles, sidebar label, comparison-section product reference, the kept aphorism). Specific corrections: - VPN reach in local mode — your laptop reaches managed services via VPN; the MCP itself talks to the Zerops API directly. - Agent-workflow Workspace layer — in hosted mode the wrapping service also runs the bundled agent CLI; in local mode the agent runs in your editor and talks to the MCP. - Trust-model isolation flags — `envIsolation: none` / `sshIsolation: vpn service@zcp` are scoped to the `zcp@1` service, not "ZCP itself". - "Where ZCP runs" architecture — hosted on the project network vs local binary talking to the API with VPN as a separate concern. Linking: - features/coding-agents — CustomCard pairs in "Two ways to run it" now link to setup pages directly. MCP-and-permissions cards link to the named tool sections in agent-workflow. Trust model linked inline. Glossary added to the bottom DocCardList. Quickstart promoted from bottom-only to a body mention. - features/local-remote-development — Browser Cloud IDE section links to Provision a hosted workspace; Picking a mode footer links to Choose your workspace; Next steps expanded with hosted-workspace, choose-workspace, glossary. Other: - Public-preview/dev-staging conflation removed across pages — the dev/staging rule is a security principle, independent of preview status. - Recipe-UI gotchas (deploy-button label) cut from quickstart and hosted-workspace — that's Zerops recipe UX, not a ZCP gotcha. - Glossary moved to end of ZCP sidebar. - Sidebar label: "Zerops Control Plane" → "Zerops Control Plane MCP". - Table-balancing CSS extended to /features/coding-agents and /features/local-remote-development. - `zerops.yml` → `zerops.yaml` (canonical). Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/docs/content/features/coding-agents.mdx | 33 ++++++----- .../features/local-remote-development.mdx | 55 ++++++++++--------- .../docs/content/zcp/concept/how-it-works.mdx | 26 ++++----- apps/docs/content/zcp/glossary.mdx | 2 +- apps/docs/content/zcp/overview.mdx | 2 +- apps/docs/content/zcp/quickstart.mdx | 7 +-- .../content/zcp/reference/agent-workflow.mdx | 8 +-- .../content/zcp/reference/troubleshooting.mdx | 6 +- .../zcp/security/production-policy.mdx | 17 ++---- .../security/tokens-and-project-access.mdx | 36 ++++++------ .../docs/content/zcp/security/trust-model.mdx | 34 ++++++------ .../content/zcp/setup/choose-workspace.mdx | 2 +- .../content/zcp/setup/hosted-workspace.mdx | 1 - .../content/zcp/setup/local-agent-bridge.mdx | 28 +++++----- .../zcp/workflows/build-and-verify-app.mdx | 6 +- .../zcp/workflows/delivery-handoff.mdx | 10 ++-- apps/docs/sidebars.js | 20 +++---- apps/docs/src/css/custom.css | 18 ++++-- 18 files changed, 158 insertions(+), 153 deletions(-) diff --git a/apps/docs/content/features/coding-agents.mdx b/apps/docs/content/features/coding-agents.mdx index c8bfc10d..f5bb9499 100644 --- a/apps/docs/content/features/coding-agents.mdx +++ b/apps/docs/content/features/coding-agents.mdx @@ -11,8 +11,8 @@ Coding agents need somewhere to operate, not just somewhere to generate code. Th > The LLM does the engineering. The developer holds intent and judgment. ZCP holds reality. -:::caution Public preview — keep production in a separate project -ZCP carries a project-scoped token with full rights so the agent can operate the services you select. It belongs in a development or staging project, not a production one. See [Production boundary](/zcp/security/production-policy). +:::caution Keep production in a separate project +The `zcp` binary runs with a project-scoped token that grants the agent full rights inside that project. It belongs in a development or staging project, not a production one — independent of preview status, you don't want a coding agent loose against the project that runs your production traffic. See [Production boundary](/zcp/security/production-policy). ::: ## Who it's for @@ -23,7 +23,7 @@ Today ZCP supports **Claude Code**, paired with a **Claude Pro or Max subscripti ## What ZCP MCP does -The ZCP MCP exposes a fixed set of operations on one Zerops project, grouped by user job. The agent never has to be told what the project looks like — ZCP reports it from what's deployed and running right now. +The ZCP MCP exposes a fixed set of operations on one Zerops project, grouped by user job. The agent never has to be told what the project looks like — the MCP reports it from what's deployed and running right now. | Job | What it means | Reference | |---|---|---| @@ -40,20 +40,20 @@ Full contract — bootstrap routes, mode semantics, failure categories, recovery ## Two ways to run it -ZCP is a binary; the question is where it runs. +ZCP MCP is a binary; the question is where it runs.
- A Zerops service (`zcp@1`) that runs the ZCP MCP inside the project, with optional Cloud IDE and bundled Claude Code. Safe by design — no path to your laptop or other projects. **Recommended starting point.** + A Zerops service (`zcp@1`) that runs the ZCP MCP inside the project, with optional Cloud IDE and bundled Claude Code. Safe by design — no path to your laptop or other projects. **Recommended starting point.** [Set up →](/zcp/setup/hosted-workspace) - The `zcp` binary on your laptop, connected to the project VPN. The agent runs in your editor. Real and supported, but the most WIP path — expect setup churn between releases. + The `zcp` binary on your laptop, connected to the project VPN. The agent runs in your editor. Real and supported, but the most WIP path — expect setup churn between releases. [Set up →](/zcp/setup/local-agent-bridge)
-Either way, the agent gets the same project-scoped operations against the same project. The hosted service packages ZCP with an editor and an agent CLI; the local bridge is the same MCP without those extras. +Either way, the agent gets the same project-scoped operations against the same project. The hosted service packages the MCP with an editor and an agent CLI; the local bridge is the same MCP without those extras. -Decision: [Choose your workspace](/zcp/setup/choose-workspace). Broader local-dev story (humans, not just agents): [Local & Remote Development](/features/local-remote-development). +Decision: [Choose your workspace](/zcp/setup/choose-workspace). First time? Try the [Quickstart](/zcp/quickstart) — no install needed. Broader local-dev story (humans, not just agents): [Local & Remote Development](/features/local-remote-development). ## What the hosted workspace adds @@ -63,7 +63,9 @@ When you pick the hosted path, ZCP MCP ships as part of a `zcp@1` Zerops service **Cloud IDE.** Browser-based VS Code (code-server) with the Claude Code plugin, SSHFS access to runtime services in the project, and a curated dev toolchain. Reachable VPN-only or on a `.zerops.app` subdomain gated by an auto-generated password. Useful when you want to watch the agent work and step in occasionally rather than running it autonomously over SSH. Full toolchain inventory: [Local & Remote Development → Browser Cloud IDE on ZCP](/features/local-remote-development#browser-cloud-ide-on-zcp). -Without either toggle, the hosted ZCP service is an MCP endpoint plus a Linux dev container with `zcli`, the platform-injected token, and project-private network reach — useful as a shared remote workstation. With both, it's a one-click agent + IDE workspace inside the project. +Without either toggle, the `zcp@1` service is an MCP endpoint plus a Linux dev container with `zcli`, the platform-injected token, and project-private network reach — useful as a shared remote workstation. With both, it's a one-click agent + IDE workspace inside the project. + +Both toggles are configured when you provision the service: [Provision a hosted workspace → Path B](/zcp/setup/hosted-workspace#path-b--toggle-the-zcp-service). ## Why transparent infrastructure works for agents @@ -83,17 +85,17 @@ ZCP MCP groups its operations into three categories so a host agent can gate the
- Inspect state and configuration. Safe to auto-allow. Example: `zerops_logs`, `zerops_events`, `zerops_discover`. + Inspect state and configuration. Safe to auto-allow. Example: `zerops_logs`, `zerops_events`, `zerops_discover`. [Full list →](/zcp/reference/agent-workflow#read-only--inspect-state-and-configuration) - Mutate the project or its services. Require confirmation. Example: `zerops_deploy`, `zerops_scale`, `zerops_delete`. + Mutate the project or its services. Require confirmation. Example: `zerops_deploy`, `zerops_scale`, `zerops_delete`. [Full list →](/zcp/reference/agent-workflow#destructive--mutate-the-project-or-its-services) - Set up infrastructure or coordinate work. Gated by team policy. Example: `zerops_recipe`, `zerops_mount`, `zerops_subdomain`. + Set up infrastructure or coordinate work. Gated by team policy. Example: `zerops_recipe`, `zerops_mount`, `zerops_subdomain`. [Full list →](/zcp/reference/agent-workflow#operational--set-up-or-coordinate-work)
-That's the surface. The boundary is the project. ZCP's token authorizes one project, full stop — cross-project, account-wide, or organization actions stay outside reach. Project roles (Owner, Admin, Developer, Guest) apply to ZCP itself; see [Roles & Permissions](/features/rbac). +That's the surface. The boundary is the project. The MCP's token authorizes one project, full stop — cross-project, account-wide, or organization actions stay outside reach. Project roles (Owner, Admin, Developer, Guest) apply to the project token the MCP uses; see [Roles & Permissions](/features/rbac). Full blast-radius framing: [Trust model](/zcp/security/trust-model). Full tool list and permission semantics: [agent workflow reference](/zcp/reference/agent-workflow). Audit surface — deploys, events, logs, git history — is the same any human session leaves: [Auditing agent work](/zcp/security/auditing-agent-work). Token shape and confirmation gates: [Tokens and credentials](/zcp/security/tokens-and-project-access). @@ -105,7 +107,7 @@ The local agent bridge isn't extensible the same way; the binary on your laptop ## Source control -When the agent runs in the hosted workspace, it lives in a container, not on your laptop — git access wires through `gh auth login` (the GitHub CLI is preinstalled), through a fine-grained token stored in ZCP's secret env vars, or through SSH agent forwarding from your local editor. When the agent runs in the local bridge, it uses your existing git credentials. Detail: [Tokens and credentials → Git credentials](/zcp/security/tokens-and-project-access#git-credentials--separate-from-zcp_api_key). +When the agent runs in the hosted workspace, it lives in a container, not on your laptop — git access wires through `gh auth login` (the GitHub CLI is preinstalled), through a fine-grained token stored in the `zcp` service's secret env vars, or through SSH agent forwarding from your local editor. When the agent runs in the local bridge, it uses your existing git credentials. Detail: [Tokens and credentials → Git credentials](/zcp/security/tokens-and-project-access#git-credentials--separate-from-zcp_api_key). ## How this differs from related tools @@ -134,8 +136,9 @@ The agent space contains several products that look superficially similar but so diff --git a/apps/docs/content/features/local-remote-development.mdx b/apps/docs/content/features/local-remote-development.mdx index b143dfa1..ff463889 100644 --- a/apps/docs/content/features/local-remote-development.mdx +++ b/apps/docs/content/features/local-remote-development.mdx @@ -5,10 +5,10 @@ description: Three ways to develop on Zerops — local with VPN, the browser Clo You can develop on Zerops without leaving your laptop, fully inside Zerops, or anywhere in between. The project's private network is the thing you join — what you use to join it is a choice, not a constraint. Same hostnames, same credentials, same managed services, and the same deploy pipeline whether you're editing on `apidev` from your local IDE or running a hot-reload loop in a container in the cloud. -This is also what closes the gap between "works on my machine" and production. The Postgres your dev container talks to and the Postgres production talks to are the same Zerops-managed service type at different resource levels, on the same kind of private network, configured by the same `zerops.yml`. Dev isn't an approximation of prod — it's prod with smaller numbers. +This is also what closes the gap between "works on my machine" and production. The Postgres your dev container talks to and the Postgres production talks to are the same Zerops-managed service type at different resource levels, on the same kind of private network, configured by the same `zerops.yaml`. Dev isn't an approximation of prod — it's prod with smaller numbers. :::note -ZCP (Zerops Control Plane) is a service you can add to any project. Two of the three modes below run inside it. The third — local development with `zcli vpn up` — doesn't require ZCP at all. +ZCP (Zerops Control Plane) hosted workspace is a `zcp@1` service you can add to any project — it bundles a Linux dev container with a curated toolchain and an optional Cloud IDE. Two of the three modes below run inside it. The third — local development with `zcli vpn up` — doesn't require ZCP at all. ::: ## How development on Zerops works @@ -17,9 +17,9 @@ Every project ships with a private network. Services inside it reach each other The development question is just: how do you, the developer, get onto that network? Three answers, all equivalent in capability. -- **Local + VPN.** Your laptop joins the private network via WireGuard. Editor, toolchain, and processes stay where they are. ZCP is not required. +- **Local + VPN.** Your laptop joins the private network via WireGuard. Editor, toolchain, and processes stay where they are. The hosted workspace isn't required. - **Browser Cloud IDE on ZCP.** A Linux container in the project, accessed through VS Code Server in the dashboard. Zero local setup. -- **Native IDE over SSH.** SSH from your local VS Code, JetBrains Gateway, Cursor, or plain `ssh` — to ZCP, or directly to a service container. +- **Native IDE over SSH.** SSH from your local VS Code, JetBrains Gateway, Cursor, or plain `ssh` — to the `zcp` service, or directly to a service container. Same `db:5432`, same `api:3000`, same credentials in all three. Switch modes without changing anything about the project. @@ -37,7 +37,7 @@ ssh apidev What you get on your laptop: every service in the project addressable by hostname, passwordless SSH into any container, real Postgres with prod-shaped data, real Redis, real S3-compatible storage. No mocks. No Docker Compose drift between what you run locally and what production runs. -What you don't have to do: install ZCP, run `zcli push`, learn a new editor, change your dev loop. If you're used to running `npm run dev` against a local Postgres, run it against `db:5432` instead — same connection string format, same SQL, same managed instance type as the one production will use. +What you don't have to do: add the hosted workspace, run `zcli push`, learn a new editor, change your dev loop. If you're used to running `npm run dev` against a local Postgres, run it against `db:5432` instead — same connection string format, same SQL, same managed instance type as the one production will use. This mode is also where coding agents running on your laptop fit. A local Claude Code or Cursor session inherits the VPN's access, so the agent can hit `db:5432` and SSH into containers the same way you can. It can consume managed services, but it can't operate the project — see [Infrastructure for Coding Agents](/features/coding-agents) for that. @@ -51,9 +51,9 @@ See the [VPN reference guide](/references/networking/vpn). **Best for:** zero local setup, working from a machine that isn't yours, or wanting a pristine environment that doesn't touch your laptop. -When you add ZCP to a project, you get an Ubuntu container running the `zcp@1` base image with a curated dev toolchain — `git`, `gh`, `jq`, `yq`, `ripgrep`, `fd`, `fzf`, `bat`, `tree`, `tmux`, `htop`, `ncdu`, `httpie`, `make`, `psql`, `mysql`, `redis-cli`, `chrome` and `puppeteer`, `sshfs`, `zsh` with Oh My Zsh, plus `zcli` and the Zerops MCP server. Optionally, browser-based VS Code (the Cloud IDE) with a Claude Code plugin and an agentic CLI bundled in. +When you add the hosted workspace (a `zcp@1` service) to a project — see [Provision a hosted workspace](/zcp/setup/hosted-workspace) — you get an Ubuntu container running the `zcp@1` base image with a curated dev toolchain — `git`, `gh`, `jq`, `yq`, `ripgrep`, `fd`, `fzf`, `bat`, `tree`, `tmux`, `htop`, `ncdu`, `httpie`, `make`, `psql`, `mysql`, `redis-cli`, `chrome` and `puppeteer`, `sshfs`, `zsh` with Oh My Zsh, plus `zcli` and the Zerops MCP server. Optionally, browser-based VS Code (the Cloud IDE) with a Claude Code plugin and an agentic CLI bundled in. -ZCP also mounts your dev services over SSHFS, so you can edit code that runs in another container as if it were local: +The hosted workspace also mounts your dev services over SSHFS, so you can edit code that runs in another container as if it were local: ```bash ls /var/www/apidev @@ -61,19 +61,19 @@ ls /var/www/frontenddev vim /var/www/apidev/src/server.ts ``` -The dev server in the mounted container hot-reloads, the database is the same `db:5432` you'd use in any other mode, and `zcli push --service apistage` deploys to staging through the production pipeline. One ZCP can mount multiple dev services at once, so a single workspace can cover the whole stack. +The dev server in the mounted container hot-reloads, the database is the same `db:5432` you'd use in any other mode, and `zcli push --service apistage` deploys to staging through the production pipeline. One hosted workspace can mount multiple dev services at once, covering the whole stack. The Cloud IDE itself is configurable. Two access methods: - **VPN Only.** The IDE is reachable through `zcli vpn up`, no public exposure. Most secure. - **Public Access.** The IDE is reachable on a `.zerops.app` subdomain, gated by an auto-generated password. -**Source control.** ZCP isn't on your laptop, so your local SSH keys don't follow it. Two patterns work: +**Source control.** The hosted workspace isn't on your laptop, so your local SSH keys don't follow it. Two patterns work: - **GitHub via `gh`.** The GitHub CLI is preinstalled. Run `gh auth login` once and `gh` handles HTTPS auth, PR creation, and review flows from the container. -- **Token in env vars.** Save a fine-grained personal access token to ZCP's secret environment variables; `git` reads it through a credential helper. +- **Token in env vars.** Save a fine-grained personal access token to the `zcp` service's secret environment variables; `git` reads it through a credential helper. -Either pattern keeps your token in ZCP, not in your project's source. +Either pattern keeps your token in the `zcp` service, not in your project's source. ## Native IDE over SSH @@ -84,12 +84,12 @@ Once `zcli vpn up` is connected, every container on the project's private networ ```bash ssh apidev # any service in the project ssh frontenddev -ssh zcp # if you provisioned ZCP +ssh zcp # if you provisioned the `zcp` service ``` -In this mode, ZCP is convenient but not required. You can connect VS Code Remote SSH straight to `apidev`, edit the source there, run the dev server in the same container, and never provision ZCP at all. The project's private network does the rest. ZCP becomes a *convenience* layer: a single workspace that carries the toolchain, mounts multiple dev services over SSHFS, and has `zcli` and the MCP server preconfigured for the project. If you'd rather connect your editor directly to your runtime services and skip ZCP entirely, that works. +In this mode, the hosted workspace is convenient but not required. You can connect VS Code Remote SSH straight to `apidev`, edit the source there, run the dev server in the same container, and never provision a `zcp` service at all. The project's private network does the rest. The hosted workspace becomes a *convenience* layer: a single container that carries the toolchain, mounts multiple dev services over SSHFS, and has `zcli` and the MCP server preconfigured for the project. If you'd rather connect your editor directly to your runtime services and skip the hosted workspace entirely, that works. -**Source control.** SSH agent forwarding works the same way it does to any other machine. Add your local SSH key to your agent (`ssh-add`), and `git push` from inside `apidev` (or ZCP) uses your laptop's GitHub key over the forwarded agent socket — no token storage required. +**Source control.** SSH agent forwarding works the same way it does to any other machine. Add your local SSH key to your agent (`ssh-add`), and `git push` from inside `apidev` (or the `zcp` service) uses your laptop's GitHub key over the forwarded agent socket — no token storage required. ```ssh-config # Local ~/.ssh/config @@ -107,17 +107,19 @@ See the [SSH reference guide](/references/networking/ssh). | | Local + VPN | Cloud IDE on ZCP | Native IDE over SSH | |---|---|---|---| | Editor runs | Local | Browser | Local | -| Toolchain | Local | ZCP | ZCP or service container | +| Toolchain | Local | Hosted workspace | Hosted workspace or service container | | Dev databases | Hostname via VPN | Native, on private network | Native, on private network | -| ZCP service required | No | Yes | No (convenience) | +| Hosted workspace required | No | Yes | No (convenience) | | Git auth | Local credentials | `gh` or token in env | SSH agent forwarding | | Best for | Keeping your setup | Disposable environments | Native UX, lighter laptop | -You're not locked into a mode. The same project supports all three, and switching is free. A teammate on Local + VPN and an agent inside ZCP are hitting the same `db:5432`. +You're not locked into a mode. The same project supports all three, and switching is free. A teammate on Local + VPN and an agent inside the hosted workspace are hitting the same `db:5432`. + +For the agent-driven path, the workspace decision (hosted wrapper vs. local binary for the MCP) is on its own page: [Choose your workspace](/zcp/setup/choose-workspace). ## Same setup, dev to production -The point worth pausing on: the development environments above don't approximate production — they share infrastructure with it. Same managed Postgres, same private network, same load balancer, same `zerops.yml`. A dev project, a stage project, and a production project differ in resource allocation and access rules, not in architecture. +The point worth pausing on: the development environments above don't approximate production — they share infrastructure with it. Same managed Postgres, same private network, same load balancer, same `zerops.yaml`. A dev project, a stage project, and a production project differ in resource allocation and access rules, not in architecture. This is what makes the "but it works on my machine" failure mode hard to reach on Zerops. Your dev container's Postgres is the same managed service type production uses. Your queue is the same NATS or Kafka. Your storage is the same S3-compatible service. When code works in dev, you've already proven it works against the kind of infrastructure it'll meet in production. The remaining question is scale, not shape. @@ -125,17 +127,20 @@ This applies whether the developer is human or an agent — see [Infrastructure ## How this differs from cloud IDEs -If you've used GitHub Codespaces or Gitpod, ZCP looks similar — a Linux container in the cloud, accessed from a browser or local IDE. Two structural differences are worth understanding before you compare them. +If you've used GitHub Codespaces or Gitpod, the hosted workspace looks similar — a Linux container in the cloud, accessed from a browser or local IDE. Two structural differences are worth understanding before you compare them. **You don't have to be in the editor.** Codespaces and Gitpod are editor-first. Their value proposition assumes you're inside their environment; the database connections, dependencies, and toolchain all live there. Zerops is network-first. The Cloud IDE is one of three ways to reach the project's private network — `zcli vpn up` lets you skip the editor environment entirely and consume managed services from your existing local setup. There's no equivalent on Codespaces or Gitpod, because their architecture doesn't expose the network. -**Dev, staging, and production are the same infrastructure.** On a CDE, your dev environment is one platform and your deployment target is another — different runtimes, different databases, different networking. On Zerops, they're the same platform. Same managed Postgres, same private network, same `zerops.yml`. Differences between dev and production are resource allocation, not architecture. The "works on my machine" gap doesn't open because the machines aren't different in kind. +**Dev, staging, and production are the same infrastructure.** On a CDE, your dev environment is one platform and your deployment target is another — different runtimes, different databases, different networking. On Zerops, they're the same platform. Same managed Postgres, same private network, same `zerops.yaml`. Differences between dev and production are resource allocation, not architecture. The "works on my machine" gap doesn't open because the machines aren't different in kind. -ZCP is also just a service. It's a container running the `zcp@1` Ubuntu base image — you can SSH into it, install your own packages, run your own processes, and configure it however you want. Nothing about it is a walled garden. +The hosted workspace is just a Zerops service. It's a container running the `zcp@1` Ubuntu base image — you can SSH into it, install your own packages, run your own processes, and configure it however you want. Nothing about it is a walled garden. -## Next Steps +## Next steps -- VPN setup and troubleshooting → [VPN Reference Guide](/references/networking/vpn) -- SSH access to services → [SSH Reference Guide](/references/networking/ssh) +- VPN setup and troubleshooting → [VPN reference](/references/networking/vpn) +- SSH access to services → [SSH reference](/references/networking/ssh) - Build and deploy from any of these environments → [Prepare, Build, Deploy Pipeline](/features/pipeline) -- Coding agents on Zerops → [Infrastructure Platform for Coding Agents](/features/coding-agents) \ No newline at end of file +- Coding agents on Zerops → [Infrastructure for Coding Agents](/features/coding-agents) +- Provision the hosted workspace → [Provision a hosted workspace](/zcp/setup/hosted-workspace) +- Pick where the agent's MCP runs → [Choose your workspace](/zcp/setup/choose-workspace) +- Term reference → [ZCP Glossary](/zcp/glossary) \ No newline at end of file diff --git a/apps/docs/content/zcp/concept/how-it-works.mdx b/apps/docs/content/zcp/concept/how-it-works.mdx index ca738b4b..dd2717fe 100644 --- a/apps/docs/content/zcp/concept/how-it-works.mdx +++ b/apps/docs/content/zcp/concept/how-it-works.mdx @@ -3,11 +3,11 @@ title: "How ZCP works" description: "ZCP MCP runs near the project, reads live platform state, and exposes a fixed set of project-scoped operations grouped by user job." --- -Zerops Control Plane (ZCP) is an MCP server — the `zcp` binary — that exposes a fixed set of project-scoped operations to a coding agent. It runs near the project, reads live platform state, and groups operations by user job. The agent never has to be told what the project looks like; ZCP reports it from what's deployed and running right now. +Zerops Control Plane (ZCP) is an MCP server — the `zcp` binary — that exposes a fixed set of project-scoped operations to a coding agent. It runs near the project, reads live platform state, and groups operations by user job. The agent never has to be told what the project looks like; the MCP reports it from what's deployed and running right now. ## Where ZCP runs -ZCP joins the project's private network — either as part of a hosted Zerops service inside the project, or as a binary on your laptop while you're connected to the project VPN. +ZCP MCP runs near the project — either as part of a hosted `zcp@1` service inside the project (on the project's private network), or as a binary on your laptop talking to the Zerops API directly while your laptop has VPN access for any local-dev work that needs to reach project services by hostname. ```mermaid flowchart TB @@ -35,19 +35,19 @@ flowchart TB ``` - **Hosted workspace.** A Zerops service of type `zcp@1` runs the ZCP MCP inside the project, with optional bundled extras: a coding-agent CLI (Claude Code) and a browser-based VS Code (the Cloud IDE) with SSHFS access to runtime services and a curated dev toolchain. Created when you pick the **AI Agent** environment from a [Zerops recipe](https://app.zerops.io/recipes), or check **Add Zerops Control Plane (ZCP) service** during project creation. -- **Local agent bridge.** Run `zcp init` in your project directory and the `zcp` binary runs on your laptop. The agent runs in your editor; ZCP talks to the Zerops API on your behalf, project services are reached over your Zerops VPN. No editor, no SSHFS, no bundled agent — just the MCP. +- **Local agent bridge.** Run `zcp init` in your project directory and the `zcp` binary runs on your laptop. The agent runs in your editor and talks to the MCP; the MCP talks to the Zerops API on your behalf. Your laptop reaches project services over the Zerops VPN for anything outside the MCP (local dev server, manual `psql`, etc.). No editor, no SSHFS, no bundled agent. The MCP operations are the same in both modes. What differs is what's bundled around the MCP: the hosted service adds editor, agent, SSHFS mounts, server-side batch deploys, and a dev-server runner; the local bridge adds deploys from your working directory and `.env` generation for local apps. Decision: [Choose your workspace](/zcp/setup/choose-workspace). ## Reads live platform state -When the agent needs context, ZCP queries the platform — what services exist, what state they're in, what ports and env variables they expose. Build and deploy timelines come from the events surface, which the agent reads when diagnosing a failure or polling a git-push deploy. No long prompt, no stale documentation; the answer comes from the project as it stands. +When the agent needs context, the MCP queries the platform — what services exist, what state they're in, what ports and env variables they expose. Build and deploy timelines come from the events surface, which the agent reads when diagnosing a failure or polling a git-push deploy. No long prompt, no stale documentation; the answer comes from the project as it stands. This is what makes three things work: -- **Cold start.** ZCP discovers what's already provisioned instead of asking you to describe it. -- **Failure diagnosis.** ZCP reads the build logs, runtime logs, and event timeline directly, classifies the failure, and proposes the next step. -- **Session resume.** After an interruption, the agent asks ZCP for status and resumes from real state, not from chat memory. +- **Cold start.** The MCP discovers what's already provisioned instead of asking you to describe it. +- **Failure diagnosis.** The MCP reads the build logs, runtime logs, and event timeline directly, classifies the failure, and proposes the next step. +- **Session resume.** After an interruption, the agent asks the MCP for status and resumes from real state, not from chat memory. ## What ZCP MCP lets the agent do @@ -55,11 +55,11 @@ Operations are grouped by user job. You ask for an outcome in plain language; th ### Discover -Read the project before changing anything: which services exist, what state they're in, what ports and env variables they expose. When you ask "what is in this project?", you don't describe it — the agent asks ZCP and tells you. +Read the project before changing anything: which services exist, what state they're in, what ports and env variables they expose. When you ask "what is in this project?", you don't describe it — the agent asks the MCP and tells you. ### Deploy -Ship code through the standard Zerops [build and deploy pipeline](/guides/deployment-lifecycle). For direct deploys the call blocks until the build finishes and the runtime reports active — the agent waits with you, not for you. For git-push delivery, ZCP returns after the push lands and the agent polls events until the runtime is active and records the deploy. +Ship code through the standard Zerops [build and deploy pipeline](/guides/deployment-lifecycle). For direct deploys the call blocks until the build finishes and the runtime reports active — the agent waits with you, not for you. For git-push delivery, the MCP returns after the push lands and the agent polls events until the runtime is active and records the deploy. When several services ship together, the hosted workspace coordinates server-side batch deploys. The local bridge runs them serially from your working directory. @@ -73,23 +73,23 @@ If reachability passes, the agent layers behavior verification on top — openin For service lifecycle — start, stop, restart, reload, [scaling](/features/scaling), [public access](/features/access) — the agent uses scoped operations that change one project at a time. [Environment variables](/features/env-variables) are read and set at service or project scope with preprocessor support, so the agent can generate strong secrets in place. -When ZCP runs in the hosted workspace, the wrapping Zerops service also gives the agent SSHFS access to other services and the option to run a long-lived dev process inside another container. Those are workspace features, not MCP operations. +When the MCP runs in a hosted workspace, the wrapping `zcp@1` service also gives the agent SSHFS access to other services and the option to run a long-lived dev process inside another container. Those are workspace features, not MCP operations. ### Recover Every failed deploy carries a structured failure classification: category, likely cause, suggested next action. The agent reads this before the raw logs. -If the agent loses context — long session, browser crash, closed tab — it asks ZCP for status and resumes from current platform state. Recovery doesn't depend on the prior chat. +If the agent loses context — long session, browser crash, closed tab — it asks the MCP for status and resumes from current platform state. Recovery doesn't depend on the prior chat. ### Hand off -When the work is done, you and the agent decide how the next change ships: keep deploying directly, commit and push to git, or hand off to your CI. ZCP records the choice and configures any push credentials and build integrations needed. See [Choose how finished work ships](/zcp/workflows/delivery-handoff). +When the work is done, you and the agent decide how the next change ships: keep deploying directly, commit and push to git, or hand off to your CI. The MCP records the choice and configures any push credentials and build integrations needed. See [Choose how finished work ships](/zcp/workflows/delivery-handoff). For a different kind of handoff — turning a deployed service into reusable import files — see [Package a running service](/zcp/workflows/package-running-service). ## Project boundary and confirmation gates -Each operation knows the project boundary. ZCP can't reach a different project or push code your token doesn't authorize. In the local bridge, ZCP itself stays project-scoped, but the agent process inherits your user; what the client does on your laptop follows your client's permissions, not ZCP's. Full picture: [Trust model](/zcp/security/trust-model). +Each operation knows the project boundary. The MCP can't reach a different project or push code your token doesn't authorize. In the local bridge, the MCP itself stays project-scoped, but the agent process inherits your user; what the client does on your laptop follows your client's permissions, not the MCP's. Full picture: [Trust model](/zcp/security/trust-model). Two operations carry an explicit confirmation gate because the loss isn't reversible from inside the conversation: diff --git a/apps/docs/content/zcp/glossary.mdx b/apps/docs/content/zcp/glossary.mdx index a4e747cf..6f4b38b1 100644 --- a/apps/docs/content/zcp/glossary.mdx +++ b/apps/docs/content/zcp/glossary.mdx @@ -9,7 +9,7 @@ Quick definitions for the terms that appear across ZCP docs. The four sections b **Zerops Control Plane (ZCP)** — the umbrella name. In context, can refer to the binary, the operations it exposes, or loosely to the wrapping Zerops service. When precision matters, use the more specific terms below. -**ZCP MCP** — the project-scoped operations the `zcp` binary exposes to a coding agent over MCP: discover, deploy, verify, operate, recover, hand off. Same MCP surface whether ZCP runs hosted or local. +**ZCP MCP** — the project-scoped operations the `zcp` binary exposes to a coding agent over MCP: discover, deploy, verify, operate, recover, hand off. Same MCP surface in both modes (hosted workspace and local agent bridge). **`zcp` binary** — the executable. Runs as a process inside the hosted service, or on your laptop in the local agent bridge. diff --git a/apps/docs/content/zcp/overview.mdx b/apps/docs/content/zcp/overview.mdx index 5801732e..7d900655 100644 --- a/apps/docs/content/zcp/overview.mdx +++ b/apps/docs/content/zcp/overview.mdx @@ -5,7 +5,7 @@ description: "What ZCP docs cover, where to start, and what stays in the platfor This section documents Zerops Control Plane (ZCP) for developers already using it. For the positioning — what ZCP is, why it exists, how it differs from sandboxes, hosted IDEs, and one-shot generators — start at [Infrastructure for Coding Agents](/features/coding-agents). -ZCP is in public preview — use it for dev and staging projects, keep production in a separate Zerops project. The local agent bridge is the more WIP of the two paths; see [Choose your workspace](/zcp/setup/choose-workspace). +ZCP is in public preview; expect setup details to change between releases. The local agent bridge is the more WIP of the two paths — see [Choose your workspace](/zcp/setup/choose-workspace). ## Where to start diff --git a/apps/docs/content/zcp/quickstart.mdx b/apps/docs/content/zcp/quickstart.mdx index 611f319a..2b6923aa 100644 --- a/apps/docs/content/zcp/quickstart.mdx +++ b/apps/docs/content/zcp/quickstart.mdx @@ -78,7 +78,6 @@ If something is missing or broken, see [Troubleshooting](/zcp/reference/troubles ## Gotchas -1. **The deploy button label is environment-specific.** On the recipe page, picking the **AI Agent** environment changes the button to `Deploy -agent`. If the button still says `Deploy ` (no `-agent` suffix), the AI Agent environment is not selected and ZCP will not be added. Pick the environment first, then deploy. -2. **The first deploy goes through `appdev`.** If the agent skips dev and tries stage first, that is a wrong path — see [workspace shapes in Choose your workspace](/zcp/setup/choose-workspace#workspace-shapes) for why. -3. **A successful deploy with a broken page is not "done".** Verify the requested behavior, not just the runtime status. -4. **Production lives in a different project.** Do not run this Quickstart in a project that hosts your production app. See [Production boundary](/zcp/security/production-policy). +- **The first deploy goes through `appdev`.** If the agent skips dev and tries stage first, that's a wrong path — see [workspace shapes](/zcp/setup/choose-workspace#workspace-shapes) for why. +- **A successful deploy with a broken page is not "done".** Verify the requested behavior, not just the runtime status. +- **Don't run this in a project that hosts your production app.** See [Production boundary](/zcp/security/production-policy). diff --git a/apps/docs/content/zcp/reference/agent-workflow.mdx b/apps/docs/content/zcp/reference/agent-workflow.mdx index 78daf49b..9a43e135 100644 --- a/apps/docs/content/zcp/reference/agent-workflow.mdx +++ b/apps/docs/content/zcp/reference/agent-workflow.mdx @@ -154,11 +154,11 @@ Safe to call without confirmation. Integration tokens can be scoped to read-only | `zerops_events` | Project event timeline (deploys, builds, scaling, status transitions), scoped by hostname | | `zerops_discover` | Read services, ports, env-var keys, current state from live platform reality | | `zerops_verify` | Post-deploy reachability and HTTP-probe checks | -| `zerops_knowledge` | Surface ZCP-side guidance specific to current project state and step | +| `zerops_knowledge` | Surface MCP-side guidance specific to current project state and step | ### Destructive — mutate the project or its services -Host agent should require user confirmation. Two operations carry an explicit ZCP-side gate (see Confirmation gates). +Host agent should require user confirmation. Two operations carry an explicit MCP-side gate (see Confirmation gates). | Tool | Purpose | |---|---| @@ -182,9 +182,9 @@ Host agent decides per team policy. These don't fit cleanly as read-only or dest ## Confirmation gates -Two operations carry an explicit ZCP-side confirmation gate because the loss isn't reversible from inside the conversation: +Two operations carry an explicit MCP-side confirmation gate because the loss isn't reversible from inside the conversation: -- **Service deletion** — requires explicit user approval in the same conversation, by service name. Hosted ZCP additionally hard-blocks deleting the workspace it's running on (`SELF_SERVICE_BLOCKED`). +- **Service deletion** — requires explicit user approval in the same conversation, by service name. The hosted MCP additionally hard-blocks deleting the `zcp` service it's running on (`SELF_SERVICE_BLOCKED`). - **Wholesale service replacement** (import override) on a service with prior failed deploy history — first call is refused with a structured payload; second call must echo it back. Detail: [Tokens and credentials → Confirmation gates](/zcp/security/tokens-and-project-access#confirmation-gates-for-destructive-actions). diff --git a/apps/docs/content/zcp/reference/troubleshooting.mdx b/apps/docs/content/zcp/reference/troubleshooting.mdx index 6f2950c8..fa6e5631 100644 --- a/apps/docs/content/zcp/reference/troubleshooting.mdx +++ b/apps/docs/content/zcp/reference/troubleshooting.mdx @@ -139,7 +139,7 @@ The agent runs the equivalent operation, waits for HTTP-readiness, and reports t ### Token errors -Token rejection happens at startup. ZCP refuses to come up with a token that has the wrong shape, and the error names the specific reason. +Token rejection happens at startup. The MCP refuses to start with a token that has the wrong shape, and the error names the specific reason. | Symptom | Likely cause | Next move | |---|---|---| @@ -148,7 +148,7 @@ Token rejection happens at startup. ZCP refuses to come up with a token that has | `No authentication found: set ZCP_API_KEY or log in with zcli` | No token is reaching ZCP. | Add `ZCP_API_KEY` under the `env` block of `.mcp.json`, or run `zcli login `. | | API replies with 401 / `AUTH_TOKEN_EXPIRED` | The token was revoked or has expired. | Generate a new project-scoped token, replace the value, and restart the agent. | -After replacing the token in `.mcp.json`, restart the agent. ZCP reads `ZCP_API_KEY` once at process startup; the live session still holds the old value in memory. +After replacing the token in `.mcp.json`, restart the agent. The MCP reads `ZCP_API_KEY` once at process startup; the live session still holds the old value in memory. ### VPN errors (local install) @@ -207,4 +207,4 @@ Status is not just for cold starts. Any time the conversation drifts from the pr - [Tokens and credentials](/zcp/security/tokens-and-project-access) — full token shape, where it lives, and how it relates to git credentials. - [VPN](/references/networking/vpn) — Zerops project VPN reference (used by the local agent bridge). - [Public access](/guides/public-access) — public URL platform behavior, custom domains, and DNS. -- [Deployment lifecycle](/guides/deployment-lifecycle) — the build, deploy, and event surface ZCP reads from. +- [Deployment lifecycle](/guides/deployment-lifecycle) — the build, deploy, and event surface the MCP reads from. diff --git a/apps/docs/content/zcp/security/production-policy.mdx b/apps/docs/content/zcp/security/production-policy.mdx index 370dba7a..a86265cd 100644 --- a/apps/docs/content/zcp/security/production-policy.mdx +++ b/apps/docs/content/zcp/security/production-policy.mdx @@ -5,26 +5,19 @@ description: "Production lives in a separate Zerops project that doesn't have a Production lives in a separate Zerops project that doesn't have a `zcp` service. ZCP is for development and staging — packaging a verified ZCP project for production handoff goes through [Package a running service](/zcp/workflows/package-running-service), which produces a re-importable bundle whose `buildFromGit:` URL points at the same source repo. Promotion runs via CI or a release pipeline. -This is **policy, not enforcement**. The platform doesn't stop you from adding ZCP to a production project; the project-scoped token enforces isolation. Treat the dev/staging boundary as load-bearing because crossing it puts the agent inside production, not because Zerops will refuse the configuration. +This is **policy, not enforcement**. The platform doesn't stop you from adding a `zcp` service to a production project; the project-scoped token enforces isolation. Treat the dev/staging boundary as load-bearing because crossing it puts an agent inside production, not because Zerops will refuse the configuration. The principle holds whether ZCP is GA or in preview — preview just adds setup churn on top of it. -## Why public preview implies dev/staging - -ZCP ships under the public preview model: - -- **Setup details may change.** `.mcp.json`, the configuration surface, the recipe environment menu, the available agents — any of these can shift between previews. Pin ZCP to development and staging projects so a change between releases doesn't put a production service in transition. -- **Errors surface against real platform calls.** Public preview means real deploys, real services, real bills, and a real platform — not a sandbox. When something goes wrong, the platform shows what actually happened. That value is the reason you don't aim it at production yet. - -## Separate production project +## Why a separate production project Keep production in a Zerops project that doesn't have a `zcp` service. Separate the agent's workspace from the running production app at the project boundary, not at the service boundary inside one project. Why a separate project: -- **Project is the security boundary.** ZCP authorizes a single project per token (see [Trust model](/zcp/security/trust-model)). A token scoped to your development project can't reach production by accident. +- **Project is the security boundary.** The MCP authorizes a single project per token (see [Trust model](/zcp/security/trust-model)). A token scoped to your development project can't reach production by accident. - **Env values stay clean.** Production env values live in production's project; development env values live in development's project. Mixing them muddies which value the agent's local `.env` actually pulls in. - **Scaling and backup policies differ.** Production typically runs HA-mode services with longer backup retention; development typically runs NON_HA with shorter retention. Separation lets [scaling](/features/scaling) and [backup](/features/backup) policies stay distinct without compromise. -In practice that means at least two projects: a development (or dev + staging) project where ZCP runs and the agent works, and a production project that doesn't have ZCP added, where deploys come from your CI or release pipeline. +In practice that means at least two projects: a development (or dev + staging) project where ZCP MCP runs and the agent works, and a production project with no `zcp` service, where deploys come from your CI or release pipeline. ## Stage as proof @@ -47,7 +40,7 @@ The handoff from development to production is **outside ZCP**. When stage verifi - Or a human runs `zcli push` against production manually, using a token scoped to production. - Or [GitHub Actions](/references/github-integration) on the production repo authenticates with a separate `ZEROPS_TOKEN` scoped to the production project (a different secret from the development workflow's). -The agent doesn't bridge the two projects. ZCP binds at startup to whatever single project its token resolves to — a token scoped to your development project can't operate on production. Promotion is a deliberate human or pipeline step. +The agent doesn't bridge the two projects. The MCP binds at startup to whatever single project its token resolves to — a token scoped to your development project can't operate on production. Promotion is a deliberate human or pipeline step. Broader CI/CD reference: [CI/CD with Zerops](/guides/ci-cd). diff --git a/apps/docs/content/zcp/security/tokens-and-project-access.mdx b/apps/docs/content/zcp/security/tokens-and-project-access.mdx index 88e6d508..081549f8 100644 --- a/apps/docs/content/zcp/security/tokens-and-project-access.mdx +++ b/apps/docs/content/zcp/security/tokens-and-project-access.mdx @@ -1,13 +1,13 @@ --- title: "Tokens and credentials" -description: "Project-scoped Zerops tokens, git credentials, and the confirmation gates ZCP enforces before destructive actions." +description: "Project-scoped Zerops tokens, git credentials, and the confirmation gates ZCP MCP enforces before destructive actions." --- -ZCP enforces project boundary at three layers — token shape (one project, exactly), where the token lives (env-injected for hosted, in `.mcp.json` for local), and per-action gates on operations that aren't reversible from inside the conversation. For the broader trust framing see [Trust model](/zcp/security/trust-model). +ZCP MCP enforces project boundary at three layers — token shape (one project, exactly), where the token lives (env-injected for hosted, in `.mcp.json` for local), and per-action gates on operations that aren't reversible from inside the conversation. For the broader trust framing see [Trust model](/zcp/security/trust-model). ## Required token shape — one project, full access -ZCP needs a Zerops API token that resolves to **exactly one project** at startup. In the dashboard, the integration-token type that produces this is **Custom access per project** scoped to a single project — that's the recommended shape. The other two types (*Full access to all projects* and *Read access to all projects*) typically resolve to more than one project on any non-trivial account; ZCP refuses to start when the resolved project count isn't one. +The MCP needs a Zerops API token that resolves to **exactly one project** at startup. In the dashboard, the integration-token type that produces this is **Custom access per project** scoped to a single project — that's the recommended shape. The other two types (*Full access to all projects* and *Read access to all projects*) typically resolve to more than one project on any non-trivial account; the MCP refuses to start when the resolved project count isn't one. To generate one: @@ -21,11 +21,11 @@ The token's blast radius equals the project. Other projects in the organization, ## Rejected token shapes -ZCP validates the token at startup and refuses the wrong shapes: +The MCP validates the token at startup and refuses the wrong shapes: -| Token shape | What ZCP does | Why | +| Token shape | What the MCP does | Why | |---|---|---| -| Multi-project (e.g. full-access) | Refuses with `TOKEN_MULTI_PROJECT` | One ZCP connection equals one project; ZCP won't pick which one. | +| Multi-project (e.g. full-access) | Refuses with `TOKEN_MULTI_PROJECT` | One MCP process equals one project; the MCP won't pick which one. | | No project access | Refuses with `TOKEN_NO_PROJECT` | The token authenticates a user but reaches no project. | | Read-only token | Fails on the first mutation | Passes startup auth; Zerops rejects the first deploy / env write / build call with a permission error. | | Expired or revoked | Refuses with `AUTH_TOKEN_EXPIRED` or `AUTH_REQUIRED` | The token no longer authenticates against the Zerops API. | @@ -37,11 +37,11 @@ Token accesses 4 projects; use project-scoped token Recovery: Create a project-scoped token in Zerops GUI or set project via zcli scope ``` -The fix is always the same: generate a per-project token and replace the value, or scope `zcli` to the target project before starting ZCP. +The fix is always the same: generate a per-project token and replace the value, or scope `zcli` to the target project before starting the MCP. ## Where the token lives — hosted vs local -ZCP reads `ZCP_API_KEY` from environment at startup. It doesn't write the token anywhere else, doesn't exchange it for a derived credential. Where the env var comes from depends on which path you're on: +The MCP reads `ZCP_API_KEY` from environment at startup. It doesn't write the token anywhere else, doesn't exchange it for a derived credential. Where the env var comes from depends on which path you're on: | Workspace | Where `ZCP_API_KEY` comes from | Who provisions it | |---|---|---| @@ -75,10 +75,10 @@ Three names, three jobs — keeping them straight prevents most credential confu | Name | What it authorizes | Where it lives | |---|---|---| | `ZCP_API_KEY` | ZCP MCP itself, against the Zerops API | Container env (hosted) or `.mcp.json` env block (local) | -| `GIT_TOKEN` | A git push from a ZCP-managed environment to a remote (GitHub, GitLab) | Hosted: project env var on the ZCP service. Local: not applicable — your git credential helper handles it. | +| `GIT_TOKEN` | A git push from the hosted workspace to a remote (GitHub, GitLab) | Hosted: project env var on the `zcp` service. Local: not applicable — your git credential helper handles it. | | `ZEROPS_TOKEN` | A GitHub Actions workflow running `zcli` against Zerops | A repository secret in GitHub Actions | -`GIT_TOKEN` only matters when delivery uses git-push and ZCP is the one pushing (the hosted path). In the local bridge, your own git CLI talks to the remote — SSH keys or credential manager already handle authentication, and ZCP doesn't need a credential of its own. If `GIT_TOKEN` is present in the hosted workspace's project env when you run git-push setup, ZCP reuses it instead of asking for a new credential. +`GIT_TOKEN` only matters when delivery uses git-push and the hosted workspace is the one pushing. In the local bridge, your own git CLI talks to the remote — SSH keys or credential manager already handle authentication, and the MCP doesn't need a credential of its own. If `GIT_TOKEN` is present in the hosted workspace's project env when you run git-push setup, the MCP reuses it instead of asking for a new credential. `ZEROPS_TOKEN` is a **Zerops API token, not a GitHub token** — the Actions workflow uses it to authenticate `zcli` against Zerops. It can be the same project-scoped token already behind `ZCP_API_KEY` (one credential, one rotation surface) or a separate token if your team prefers credential separation. The agent prepares the right `gh secret set` command for your environment: @@ -96,8 +96,8 @@ Run the command in a terminal authenticated to the GitHub repository (`gh auth l Rotate in the Zerops dashboard, then propagate to the consuming surface: -- **Hosted workspace** — the project env value updates; ZCP picks up the new value the next time the workspace boots or the `zcp` service restarts. -- **Local bridge** — paste the new token into the `env` block of `.mcp.json`, then restart Claude Code so the new ZCP process inherits the updated env. +- **Hosted workspace** — the project env value updates; the MCP picks up the new value the next time the workspace boots or the `zcp` service restarts. +- **Local bridge** — paste the new token into the `env` block of `.mcp.json`, then restart Claude Code so the new MCP process inherits the updated env. - **GitHub Actions secret** — `gh secret set ZEROPS_TOKEN ...` (or the GitHub UI) replaces the secret. Next workflow run uses the new value. Rotations on `ZCP_API_KEY` and `ZEROPS_TOKEN` are independent unless you reused the same Zerops token for both. @@ -108,7 +108,7 @@ A valid token doesn't unlock everything. Two operations carry an explicit confir | Operation | Gate | |---|---| -| **Service deletion** — removes the service entirely; container, deployed code, env vars, and (for managed services) data go with it. | The MCP tool contract requires explicit user approval in the same conversation, by service name. Supported clients (Claude Code) enforce this. ZCP itself hard-blocks one case: deleting the hosted ZCP service it's running on (`SELF_SERVICE_BLOCKED`). | +| **Service deletion** — removes the service entirely; container, deployed code, env vars, and (for managed services) data go with it. | The MCP tool contract requires explicit user approval in the same conversation, by service name. Supported clients (Claude Code) enforce this. The hosted MCP additionally hard-blocks one case: deleting the `zcp` service it's running in (`SELF_SERVICE_BLOCKED`). | | **Wholesale service replacement** (import override) on a service with prior failed deploy history | First call is refused with a structured payload naming the operation and target hostnames. The second call must echo that payload back as confirmation. | Everything else the agent runs — deploys, env changes, lifecycle actions, restarts, scaling — runs without pausing for approval. Most are reversible by another call. A few are operationally destructive even though they aren't gated (deleting an env value, rotating a secret used by running services); you stay in the loop on those through the verify pass and the audit trail, not a pre-call gate. @@ -117,7 +117,7 @@ Approval is an explicit yes paired with the service name in the same turn-window ### Diagnose before destruct -When a service is in trouble — non-running state, failed last deploy, or both — ZCP enforces one rule across destructive paths: read the platform's account of why before destroying anything. The first call against a service with failure history is refused; the agent reads the failure classification, events, and logs, then either fixes the cause or comes back with a confirmation that includes a summary of what it read. +When a service is in trouble — non-running state, failed last deploy, or both — the MCP enforces one rule across destructive paths: read the platform's account of why before destroying anything. The first call against a service with failure history is refused; the agent reads the failure classification, events, and logs, then either fixes the cause or comes back with a confirmation that includes a summary of what it read. The rule prevents two failure modes: @@ -126,15 +126,15 @@ The rule prevents two failure modes: Recovery state isn't broken state. A service that came up `READY_TO_DEPLOY` and is waiting for code isn't in trouble — the gate fires on platform-recorded failure, not on idle. -For the broader threat model and what ZCP refuses outright, see [Trust model](/zcp/security/trust-model). +For the broader threat model and what the MCP refuses outright, see [Trust model](/zcp/security/trust-model). ## Gotchas -- **Multi-project tokens are refused at startup, not at first deploy.** A full-access token won't let ZCP boot at all. Generate a per-project token before connecting. +- **Multi-project tokens are refused at startup, not at first deploy.** A full-access token won't let the MCP boot at all. Generate a per-project token before connecting. - **`zcp init` overwrites `.mcp.json`.** If you added `ZCP_API_KEY` to the file and re-run `zcp init`, the regenerated template ships without your token. Re-add it before launching the agent. -- **`GIT_TOKEN` is not `ZCP_API_KEY`.** `GIT_TOKEN` authorizes git push (hosted, when ZCP performs the push); `ZCP_API_KEY` authorizes ZCP against Zerops. Mixing them grants too much or fails authentication. +- **`GIT_TOKEN` is not `ZCP_API_KEY`.** `GIT_TOKEN` authorizes git push (hosted, when the workspace performs the push); `ZCP_API_KEY` authorizes the MCP against Zerops. Mixing them grants too much or fails authentication. - **`ZEROPS_TOKEN` in GitHub Actions is a Zerops API token, not a GitHub PAT.** If a deploy from Actions fails on permissions and you reach for a GitHub PAT, you've rotated the wrong credential. -- **Rotation is picked up on next ZCP startup, not mid-session.** Restart the agent after rotating; the live session still holds the old value in process memory. +- **Rotation is picked up on next MCP startup, not mid-session.** Restart the agent after rotating; the live session still holds the old value in process memory. - **Confirmation must be in the same conversation.** Approval from a previous chat doesn't carry forward. - **A successful confirmation is still a destructive call.** Once you acknowledge replacement, the override runs. Roll-back isn't automatic — recover lost data through [Backup](/features/backup), review what changed through [Auditing agent work](/zcp/security/auditing-agent-work). diff --git a/apps/docs/content/zcp/security/trust-model.mdx b/apps/docs/content/zcp/security/trust-model.mdx index ea071aa9..c4f05968 100644 --- a/apps/docs/content/zcp/security/trust-model.mdx +++ b/apps/docs/content/zcp/security/trust-model.mdx @@ -1,9 +1,9 @@ --- title: "Trust model" -description: "ZCP gives a coding agent project-scoped power inside one Zerops project. The hosted and local paths set different blast radii." +description: "ZCP MCP gives a coding agent project-scoped power inside one Zerops project. The hosted and local paths set different blast radii." --- -ZCP gives a coding agent project-scoped power inside one Zerops project. Same control plane, two ways to run it — and the blast radius is different. That difference is the headline of this page. +ZCP MCP gives a coding agent project-scoped power inside one Zerops project. Same MCP, two ways to run it — and the blast radius around it is different. That difference is the headline of this page. ## Hosted workspace or local agent bridge — two different blast radii @@ -34,19 +34,19 @@ flowchart LR | Where the agent runs | A `zcp@1` service container in your project | Your laptop | | What it can touch | The container, project services, project token, and (when requested) runtime filesystems via SSHFS mounts | The same project surface, **plus whatever the agent client can reach on your laptop** | | Network reach | The project's private network | Project services over VPN; everything else your laptop can already reach | -| Safety profile | **Safe by design** — structural isolation; no path to your laptop, home directory, or other projects | **Supervise the agent client** — ZCP stays project-scoped, but the agent process inherits your user | +| Safety profile | **Safe by design** — structural isolation; no path to your laptop, home directory, or other projects | **Supervise the agent client** — the MCP stays project-scoped, but the agent process inherits your user | Either path is the right choice in context. The local bridge fits when you want to keep your editor, dotfiles, and toolchain — recognize the security model and set the agent client's permissions accordingly. ## Project is the boundary -One ZCP process works against one Zerops project. The boundary starts at authentication: at startup, ZCP resolves the token to a project before it exposes operations. A token with access to no projects is refused; a token with access to multiple projects is refused. The intended shape is a Zerops token scoped to exactly one project. +One MCP process works against one Zerops project. The boundary starts at authentication: at startup, the MCP resolves the token to a project before exposing operations. A token with access to no projects is refused; a token with access to multiple projects is refused. The intended shape is a Zerops token scoped to exactly one project. -Zerops [RBAC](/features/rbac) remains the authority. If the token can deploy, manage env vars, read logs, or operate services in that project, ZCP exposes those operations. If Zerops rejects the token for an operation, ZCP can't bypass it. +Zerops [RBAC](/features/rbac) remains the authority. If the token can deploy, manage env vars, read logs, or operate services in that project, the MCP exposes those operations. If Zerops rejects the token for an operation, the MCP can't bypass it. | Question | Boundary | |---|---| -| What project can ZCP see? | The one project resolved from the token at startup. | +| What project can the MCP see? | The one project resolved from the token at startup. | | What can the agent change? | Anything the token can change inside that project: runtime services, managed services, env vars, deploys, logs, lifecycle, scaling, public access. | | What's outside reach? | Other projects, organization-wide settings, any resource Zerops RBAC doesn't grant. | | Network scope? | The project's private network. See [Public access and private networking](/features/access). | @@ -57,8 +57,8 @@ Project-scoped doesn't mean read-only. A full project token is still powerful in The hosted workspace is the safe-by-design path. A few specifics: -- **`zcp` service ≠ runtime service.** The ZCP service is the control surface; runtime services are where your app code runs. Deploys target a runtime service, not `zcp`. -- **Two relaxed isolation flags make agent work possible.** The hosted `zcp@1` service ships with `envIsolation: none` (so it can read env from other services in the project — what lets the agent connect to your databases without you copying credentials around) and `sshIsolation: vpn service@zcp` (so it can SSH into the services you select — what makes SSHFS-mounted dev work). Both are scoped to ZCP itself; other services keep their normal isolation defaults. +- **`zcp` service ≠ runtime service.** The `zcp@1` service is the control surface; runtime services are where your app code runs. Deploys target a runtime service, not the `zcp` service. +- **Two relaxed isolation flags make agent work possible.** The hosted `zcp@1` service ships with `envIsolation: none` (so it can read env from other services in the project — what lets the agent connect to your databases without you copying credentials around) and `sshIsolation: vpn service@zcp` (so it can SSH into the services you select — what makes SSHFS-mounted dev work). Both are scoped to the `zcp` service itself; other services keep their normal isolation defaults. - **Filesystem reach is narrower than network reach.** The agent reaches every project service over the private network, but only sees runtime files through SSHFS mounts when requested. `/var/www` is the SSHFS mount root, not an app repository. - **A terminal in the hosted workspace has the same project-scoped power as the agent.** Convenient, and the reason the preview guidance keeps production in a separate project. - **The editor reaches the workspace via a Zerops public subdomain.** Authentication, access logging, custom domains, and revocation follow standard [public access](/features/access) rules — the workspace isn't a special case. For VPN-only access instead, flip the per-service setting. @@ -67,22 +67,22 @@ The hosted workspace is the safe-by-design path. A few specifics: The local bridge is the supervise-the-client path. A few specifics: -- **Per-project files are isolated.** Each project directory has its own `.mcp.json` and `.zcp/state/`. Switching projects means switching directories; ZCP doesn't merge local state. -- **VPN setup is outside ZCP's authority.** Bringing up the Zerops VPN requires admin or root approval on your laptop. ZCP tells you the `zcli vpn up` command; it doesn't start the VPN. -- **`.env` is generated, not synced.** ZCP writes a snapshot when you ask for it. Regenerate after project env changes. -- **The local bridge doesn't protect your checkout from the agent client.** ZCP's boundary is the Zerops side; your client's permissions decide what happens on your laptop. +- **Per-project files are isolated.** Each project directory has its own `.mcp.json` and `.zcp/state/`. Switching projects means switching directories; the binary doesn't merge local state across them. +- **VPN setup is outside the MCP's authority.** Bringing up the Zerops VPN requires admin or root approval on your laptop. The MCP can tell you the `zcli vpn up` command; it doesn't start the VPN. +- **`.env` is generated, not synced.** The MCP writes a snapshot when you ask for it. Regenerate after project env changes. +- **The local bridge doesn't protect your checkout from the agent client.** The MCP's boundary is the Zerops side; your client's permissions decide what happens on your laptop. Full setup: [Use ZCP locally](/zcp/setup/local-agent-bridge). -## What ZCP refuses by design +## What the MCP refuses by design -ZCP uses hard refusals for boundaries that should never be inferred, and confirmation gates for actions that are destructive but sometimes necessary. +The MCP uses hard refusals for boundaries that should never be inferred, and confirmation gates for actions that are destructive but sometimes necessary. | Behavior | When it triggers | Result | |---|---|---| -| Refuse a token with no project access | Startup can't resolve any Zerops project from the token | ZCP doesn't start. Use a token with access to one project. | -| Refuse a multi-project token | Startup sees more than one accessible project | ZCP doesn't choose for the agent. Generate a project-scoped token or scope `zcli` to one project. | -| Refuse hosted self-deletion (`SELF_SERVICE_BLOCKED`) | A service-deletion call targets the service ZCP is running on | Blocked. Remove the workspace via the Zerops UI or `zcli` if you really intend to. | +| Refuse a token with no project access | Startup can't resolve any Zerops project from the token | The MCP doesn't start. Use a token with access to one project. | +| Refuse a multi-project token | Startup sees more than one accessible project | The MCP doesn't choose for the agent. Generate a project-scoped token or scope `zcli` to one project. | +| Refuse hosted self-deletion (`SELF_SERVICE_BLOCKED`) | A service-deletion call targets the service the MCP is running in | Blocked. Remove the workspace via the Zerops UI or `zcli` if you really intend to. | | Require named approval for deletion | The agent wants to delete a service | Explicit approval in the current conversation, by service name. The agent doesn't delete proactively. | | Gate destructive service replacement | An import override would replace services with prior failed deploy history | First call shows what would be destroyed and refuses; second call must acknowledge the same payload before the import proceeds. | diff --git a/apps/docs/content/zcp/setup/choose-workspace.mdx b/apps/docs/content/zcp/setup/choose-workspace.mdx index 25d6110e..726ae24f 100644 --- a/apps/docs/content/zcp/setup/choose-workspace.mdx +++ b/apps/docs/content/zcp/setup/choose-workspace.mdx @@ -44,7 +44,7 @@ Setup: [Provision a hosted workspace](/zcp/setup/hosted-workspace). Install the `zcp` binary on your laptop and run `zcp init` in your project directory. The agent runs in your editor; ZCP MCP runs as a process on your machine and talks to the Zerops API on your behalf. Your laptop is the development environment — your normal local setup with ZCP wired in. -The agent edits code in your working directory, runs your usual local dev server, deploys to Zerops through the MCP, and verifies the deployed result. Managed services (database, cache, storage) live in the project; your laptop reaches them over the Zerops VPN with credentials ZCP writes into a local `.env`. +The agent edits code in your working directory, runs your usual local dev server, deploys to Zerops through the MCP, and verifies the deployed result. Managed services (database, cache, storage) live in the project; your laptop reaches them over the Zerops VPN with credentials the MCP writes into a local `.env`. **Pick this when** diff --git a/apps/docs/content/zcp/setup/hosted-workspace.mdx b/apps/docs/content/zcp/setup/hosted-workspace.mdx index 1f239d9b..057eb0eb 100644 --- a/apps/docs/content/zcp/setup/hosted-workspace.mdx +++ b/apps/docs/content/zcp/setup/hosted-workspace.mdx @@ -89,7 +89,6 @@ Run additional processes alongside the bundled agent and Cloud IDE — a pre-war ## Gotchas -- **The recipe environment selector controls whether ZCP is added.** If the deploy button on a recipe page is `Deploy ` (no `-agent` suffix), you've got a non-AI environment selected. Reselect the **AI Agent** environment or use Path B afterward. - **The `zcp` service is not the application.** It's where the agent operates *from*; your runtime services (`appdev`, `appstage`, `app`) are where code is deployed. Ask the agent to deploy a runtime service, not `zcp`. - **Don't set `ZCP_API_KEY` by hand in the hosted workspace.** It's platform-injected. Manual override risks breaking the agent's access. (The local bridge is the only path where you manage the token yourself.) - **First load takes a minute.** Provisioning includes pulling the workspace image, starting code-server, and registering Claude Code. Until the service reaches running state, the workspace URL may return an empty page or error. diff --git a/apps/docs/content/zcp/setup/local-agent-bridge.mdx b/apps/docs/content/zcp/setup/local-agent-bridge.mdx index 9104b394..a7a4187d 100644 --- a/apps/docs/content/zcp/setup/local-agent-bridge.mdx +++ b/apps/docs/content/zcp/setup/local-agent-bridge.mdx @@ -6,15 +6,15 @@ description: "Run the zcp binary on your laptop and connect your local editor's Run the `zcp` binary on your laptop. With `zcli vpn up `, your laptop joins the project's private network — the same network your services use. The agent runs in your editor; ZCP MCP runs as a process on your machine and bridges between the two. :::warning Public preview — local mode is the part most likely to change -The `zcp` binary, the `.mcp.json` shape, and the artifacts written by `zcp init` are still settling. Pin local mode to development and staging projects, keep production in a separate project, and expect setup details to shift between releases. +The `zcp` binary, the `.mcp.json` shape, and the artifacts written by `zcp init` are still settling. Expect setup details to shift between releases. ::: The local agent bridge is for developers who already have a comfortable setup — your editor, your shell, your dev server — and want a coding agent that can drive Zerops without giving up your machine. If you're evaluating ZCP for the first time, the [hosted workspace](/zcp/setup/hosted-workspace) is a faster start. -You don't need ZCP at all to develop locally against a Zerops project — `zcli vpn up` plus your editor is enough; see [Local & Remote Development](/features/local-remote-development). ZCP is what you add when you also want a coding agent operating the project from the same machine: discovering services, generating credentials, deploying, reading logs, verifying. +You don't need ZCP at all to develop locally against a Zerops project — `zcli vpn up` plus your editor is enough; see [Local & Remote Development](/features/local-remote-development). The MCP is what you add when you also want a coding agent operating the project from the same machine: discovering services, generating credentials, deploying, reading logs, verifying. :::caution Supervise the agent client -The local bridge runs the agent on your dev machine. ZCP itself stays project-scoped, but the agent process inherits your user — what your client (Claude Code, etc.) does with file edits, shell commands, and other tools follows your client's permissions, not ZCP's. Set those permissions accordingly. See [Trust model](/zcp/security/trust-model). +The local bridge runs the agent on your dev machine. The MCP itself stays project-scoped, but the agent process inherits your user — what your client (Claude Code, etc.) does with file edits, shell commands, and other tools follows your client's permissions, not the MCP's. Set those permissions accordingly. See [Trust model](/zcp/security/trust-model). ::: ## Prerequisites @@ -22,11 +22,11 @@ The local bridge runs the agent on your dev machine. ZCP itself stays project-sc - A Zerops project. Create one from the [Zerops dashboard](https://app.zerops.io/dashboard/project-add) or from a [recipe](https://app.zerops.io/recipes). - [zCLI](/references/cli) installed on your machine and authenticated. - A **project-scoped Zerops token** (steps below). Multi-project tokens are refused at startup. -- A **Claude Pro or Max subscription** and Claude Code installed locally. Claude Code handles login through its own flow; ZCP doesn't see your subscription credentials. +- A **Claude Pro or Max subscription** and Claude Code installed locally. Claude Code handles login through its own flow; the MCP doesn't see your subscription credentials. ## Get a project-scoped Zerops token -ZCP needs a Zerops API token scoped to **one project** — multi-project and full-access tokens are refused at startup. Generate one in the Zerops dashboard, then keep the value handy for the **Add your token** step. Full walkthrough, rejection rules, and rotation: [Tokens and credentials](/zcp/security/tokens-and-project-access). +The MCP needs a Zerops API token scoped to **one project** — multi-project and full-access tokens are refused at startup. Generate one in the Zerops dashboard, then keep the value handy for the **Add your token** step. Full walkthrough, rejection rules, and rotation: [Tokens and credentials](/zcp/security/tokens-and-project-access). ## Install the binary @@ -65,17 +65,17 @@ Claude Code starts and lists the MCP servers it found. The `zerops` server (that Use ZCP to list the services in this project. ``` -A working connection answers with the project's runtime and managed services and their state. If the agent says it can't reach ZCP, see [Troubleshooting](/zcp/reference/troubleshooting) — most common issues are token-scope mismatches and starting Claude Code from the wrong directory. +A working connection answers with the project's runtime and managed services and their state. If the agent says it can't reach the MCP, see [Troubleshooting](/zcp/reference/troubleshooting) — most common issues are token-scope mismatches and starting Claude Code from the wrong directory. ## Connect the project VPN -ZCP reaches managed services (databases, caches, storage) through the Zerops project [VPN](/references/networking/vpn). Bring it up with zCLI: +Your laptop reaches managed services (databases, caches, storage) through the Zerops project [VPN](/references/networking/vpn) — the MCP itself talks to the Zerops API directly, but anything your local app or shell does against `db`, `cache`, etc. needs the tunnel. Bring it up with zCLI: ```bash zcli vpn up ``` -You'll be prompted for sudo or admin — VPN setup needs root on Linux and macOS. ZCP can't start the VPN for you; this is your one manual step. +You'll be prompted for sudo or admin — VPN setup needs root on Linux and macOS. The MCP can't start the VPN for you; this is your one manual step. After the VPN is up, project services resolve by hostname (`db`, `cache`, etc.) from your laptop. @@ -87,7 +87,7 @@ Ask Claude Code: Use ZCP to generate a .env file for my local app. ``` -ZCP reads `run.envVariables` for the requested service from your local `zerops.yaml`, resolves env-template references (the `${hostname_var}` syntax) from the platform, and writes a `.env` in your working directory. Cross-service references resolve recursively — for example `DATABASE_URL=postgres://${db_user}:${db_password}@${db_hostname}:${db_port}/${db_dbName}` lands as a fully-resolved connection string. +The MCP reads `run.envVariables` for the requested service from your local `zerops.yaml`, resolves env-template references (the `${hostname_var}` syntax) from the platform, and writes a `.env` in your working directory. Cross-service references resolve recursively — for example `DATABASE_URL=postgres://${db_user}:${db_password}@${db_hostname}:${db_port}/${db_dbName}` lands as a fully-resolved connection string. The call needs the target service hostname, a `zerops.yaml` in your working directory, and a matching `setup:` entry with non-empty `run.envVariables`. Without those, generate-dotenv returns an error instead of guessing. @@ -95,7 +95,7 @@ Your local app reads the same hostnames and credentials it would see deployed, j ## Link a deploy target -Local mode needs one Zerops runtime linked as your stage target so the agent has somewhere to deploy from your working directory. If your project has exactly one runtime, ZCP picks it up automatically. If multiple, the agent asks which to link — answer by hostname (e.g. `appstage`). +Local mode needs one Zerops runtime linked as your stage target so the agent has somewhere to deploy from your working directory. If your project has exactly one runtime, the MCP picks it up automatically. If multiple, the agent asks which to link — answer by hostname (e.g. `appstage`). If no runtime is linked yet (project has only managed services, or you haven't decided), the agent's default deploy path refuses with a hint. You can still develop locally against the managed services, and git-push delivery can still work — see [Choose how finished work ships](/zcp/workflows/delivery-handoff). Ask the agent to link a runtime by hostname when ready. @@ -103,16 +103,16 @@ Workspace shapes the agent uses here: `local-stage` once a runtime is linked, `l ## What stays your tool's job -ZCP doesn't run your local dev server. Vite, Valet, Docker Compose, your IDE's runner, whatever — the dev process stays your tool's responsibility. ZCP works alongside it. +The MCP doesn't run your local dev server. Vite, Valet, Docker Compose, your IDE's runner, whatever — the dev process stays your tool's responsibility. The MCP works alongside it. -ZCP doesn't mount Zerops runtime filesystems on your laptop. The hosted workspace gives the agent SSHFS access; the local bridge doesn't. If you need to read runtime files from your machine, use [SSH](/references/networking/ssh) directly. +The MCP doesn't mount Zerops runtime filesystems on your laptop. The hosted workspace gives the agent SSHFS access; the local bridge doesn't. If you need to read runtime files from your machine, use [SSH](/references/networking/ssh) directly. ## Gotchas - **VPN goes down with sleep.** Laptop sleep, network change, idle timeout — the tunnel often drops. Bring it back with `zcli vpn up ` before the agent retries. -- **Each project directory needs its own `zcp init`.** State and tokens are per-project. Switching projects means switching directories; Claude Code picks up the wrong ZCP connection if you launch from the wrong root. +- **Each project directory needs its own `zcp init`.** State and tokens are per-project. Switching projects means switching directories; Claude Code picks up the wrong MCP connection if you launch from the wrong root. - **Multi-project tokens are rejected at startup.** A token granting access to multiple projects (or no project) is refused. Generate a per-project token (steps above). -- **The `zerops` server name in `.mcp.json` is fixed.** Don't rename the key; ZCP and Claude Code both expect it. +- **The `zerops` server name in `.mcp.json` is fixed.** Don't rename the key; the MCP and Claude Code both expect it. - **The `.env` is a snapshot.** Regenerate after changing project env variables, not before. - **Production stays out of this loop.** Local bridge is for development and staging projects. See [Production boundary](/zcp/security/production-policy). diff --git a/apps/docs/content/zcp/workflows/build-and-verify-app.mdx b/apps/docs/content/zcp/workflows/build-and-verify-app.mdx index db238052..15364737 100644 --- a/apps/docs/content/zcp/workflows/build-and-verify-app.mdx +++ b/apps/docs/content/zcp/workflows/build-and-verify-app.mdx @@ -40,13 +40,13 @@ When code is ready, the agent runs the standard Zerops [build and deploy pipelin - **First deploy is always direct.** Delivery handoff applies to subsequent changes, not the first. - **Build is separate from runtime.** Build runs in a temporary build container; the runtime is created with the new appVersion. The agent reads logs from each phase distinctly. - **Build logs and runtime logs are different streams.** A build failure shows in build-container logs; a runtime crash shows in runtime-container logs. The agent picks the right stream for the failure category — pasting runtime logs for a build failure produces a wrong diagnosis. -- **Hosted multi-service deploys run in parallel.** When several services ship together from a hosted workspace, ZCP runs them concurrently via deploy-batch. From the local bridge, deploys run serially per service. In a dev+stage workflow, the typical order is dev first, verify, then deploy stage from the verified dev build. +- **Hosted multi-service deploys run in parallel.** When several services ship together from a hosted workspace, the MCP runs them concurrently via deploy-batch. From the local bridge, deploys run serially per service. In a dev+stage workflow, the typical order is dev first, verify, then deploy stage from the verified dev build. Deploy success and verify success aren't the same. A successful deploy means the build finished and the runtime started; whether the requested behavior works is a separate check. ## Read logs and events -When something looks wrong, ZCP gives the agent direct access to platform evidence — not your retell of it. +When something looks wrong, the MCP gives the agent direct access to platform evidence — not your retell of it. - The project activity timeline shows recent deploys, builds, status transitions, and process events, scoped by service hostname. - Runtime logs come back filtered by severity, time window, and search text — the agent does not need to scroll a wall of output. @@ -92,7 +92,7 @@ Whether the loop closed cleanly or stopped on a blocker, the platform has its ow 1. **Deploy success is not verify success.** A green build with a 500 on the route you care about isn't finished — the build status check and the behavior check are different gates, and the second is the one that matters to a user. 2. **In a `standard` (dev/stage) project, stage deploys do not happen automatically.** Dev work targets the dev runtime only — stage deploys must be asked for. If the agent silently changes the stage runtime, that is a wrong path. (`local-stage` projects deploy from your laptop into the linked stage runtime by design and are not affected — see [workspace shapes](/zcp/setup/choose-workspace#workspace-shapes).) -3. **Five attempts is your stop signal.** A loop that cannot make progress in five tries is a loop that needs you, not another retry. ZCP does not enforce this in the deploy/verify loop — it's the cadence you should expect and the threshold beyond which you step in. +3. **Five attempts is your stop signal.** A loop that can't make progress in five tries is a loop that needs you, not another retry. The MCP doesn't enforce this in the deploy/verify loop — it's the cadence you should expect and the threshold beyond which you step in. ## Next steps diff --git a/apps/docs/content/zcp/workflows/delivery-handoff.mdx b/apps/docs/content/zcp/workflows/delivery-handoff.mdx index 84c1c9ab..d4df0e79 100644 --- a/apps/docs/content/zcp/workflows/delivery-handoff.mdx +++ b/apps/docs/content/zcp/workflows/delivery-handoff.mdx @@ -1,9 +1,9 @@ --- title: "Choose how finished work ships" -description: "Choose whether ZCP closes work by deploying directly, pushing to git, or handing off to your CI." +description: "Choose whether the agent closes work by deploying directly through the MCP, pushing to git, or handing off to your CI." --- -Choose whether ZCP closes work by deploying directly, pushing to git, or handing off to your CI. Picks up after [Build and verify an app](/zcp/workflows/build-and-verify-app) — the first deploy worked, verification passed, the app does what you asked. Now decide how the next change gets out the door. +Choose whether the agent closes work by deploying directly through the MCP, pushing to git, or handing off to your CI. Picks up after [Build and verify an app](/zcp/workflows/build-and-verify-app) — the first deploy worked, verification passed, the app does what you asked. Now decide how the next change gets out the door. If you don't have a CI pipeline yet, the third answer isn't for you — pick "the agent keeps deploying" and revisit when your team needs git-tracked releases. @@ -13,7 +13,7 @@ ZCP is for dev/staging. Production handoff goes through [Package a running servi - **The agent keeps deploying directly.** Future changes ship the same way the first deploy did — straight from ZCP through the Zerops [build and deploy pipeline](/guides/deployment-lifecycle). Right for solo work and fast iteration. - **Push to git, then build from there.** Future changes get committed and pushed to a configured remote. Either Zerops or your existing CI takes the push and runs the build. Right when the team's source of truth is git and you want reviewable commits. -- **Hand off to your CI or a human.** ZCP records every deploy and verify the agent runs at your request, but does not initiate further deploys. Right when an external pipeline or release process owns delivery from here. +- **Hand off to your CI or a human.** The MCP records every deploy and verify the agent runs at your request, but doesn't initiate further deploys. Right when an external pipeline or release process owns delivery from here. You ask for an outcome ("set up git-push delivery for appdev", "my CI takes over from here"); the agent picks the right operations. Git-push capability and what fires after a push are independent — configured git-push can coexist with the agent-keeps-deploying answer for emergencies. @@ -49,7 +49,7 @@ Push credentials differ by workspace: hosted reuses or asks for a `GIT_TOKEN` (t ### What fires after a push lands -- **Nothing tracked by ZCP.** Whatever your repository already has — a non-Zerops CI, a deploy hook elsewhere — keeps running. ZCP records the deploys you ask it to verify and stays out of the way. +- **Nothing tracked by the MCP.** Whatever your repository already has — a non-Zerops CI, a deploy hook elsewhere — keeps running. The MCP records the deploys you ask it to verify and stays out of the way. - **The Zerops dashboard build integration.** The Zerops dashboard pulls the repository and runs the [build and deploy pipeline](/guides/deployment-lifecycle) — see [GitHub integration via the dashboard](/references/github-integration#integration-via-zerops-gui). - **A GitHub Actions workflow you check in.** Your repository's `.github/workflows/zerops.yml` checks out the code and pushes it to Zerops via `zcli` — see [GitHub Actions](/references/github-integration#github-workflow-integration). @@ -57,7 +57,7 @@ The GitHub Actions option needs a Zerops API token (not a GitHub token) stored a ## Hand off to your CI or a human -ZCP keeps recording every deploy and verify the agent runs at your request, but doesn't initiate further deploys on its own and doesn't auto-close the session. +The MCP keeps recording every deploy and verify the agent runs at your request, but doesn't initiate further deploys on its own and doesn't auto-close the session. Pick this when an external CI/CD pipeline owns delivery, a human takes over for a release step that doesn't belong inside the agent loop, or the next change is ambiguous enough that explicit close calls beat automation. diff --git a/apps/docs/sidebars.js b/apps/docs/sidebars.js index b6da0dda..d929d651 100644 --- a/apps/docs/sidebars.js +++ b/apps/docs/sidebars.js @@ -579,7 +579,7 @@ module.exports = { }, { type: 'category', - label: 'Zerops Control Plane', + label: 'Zerops Control Plane MCP', collapsible: false, customProps: { sidebar_is_group_headline: true, @@ -612,15 +612,6 @@ module.exports = { }, className: 'homepage-sidebar-item', }, - { - type: 'doc', - id: 'zcp/glossary', - label: 'Glossary', - customProps: { - sidebar_icon: 'book-open', - }, - className: 'homepage-sidebar-item', - }, { type: 'category', label: 'Connect', @@ -717,6 +708,15 @@ module.exports = { }, className: 'homepage-sidebar-item', }, + { + type: 'doc', + id: 'zcp/glossary', + label: 'Glossary', + customProps: { + sidebar_icon: 'book-open', + }, + className: 'homepage-sidebar-item', + }, ], }, { diff --git a/apps/docs/src/css/custom.css b/apps/docs/src/css/custom.css index 6ff6fcfb..dbcccde3 100644 --- a/apps/docs/src/css/custom.css +++ b/apps/docs/src/css/custom.css @@ -234,20 +234,26 @@ html[data-theme='dark'] .docsearch-btn:hover { @import url('./components/copy-page-menu.css'); @import url('./components/tooltip.css'); -/* ZCP docs: force balanced table column widths on tablet and up. - Default markdown tables in this section render as display:block with - browser auto-layout, which collapses one column to ~17% when the other - has bold-led short text the browser treats as max-content. Equal-width +/* ZCP + paired feature pages: force balanced table column widths on tablet + and up. Default markdown tables render as display:block with browser + auto-layout, which collapses one column to ~17% when the other has + bold-led short text the browser treats as max-content. Equal-width fixed layout keeps long-cell content readable. Below 768px we leave the default responsive overflow-x behavior alone. */ @media (min-width: 768px) { - html[class*='docs-doc-id-zcp'] .theme-doc-markdown table { + html[class*='docs-doc-id-zcp'] .theme-doc-markdown table, + html[class*='docs-doc-id-features/coding-agents'] .theme-doc-markdown table, + html[class*='docs-doc-id-features/local-remote-development'] .theme-doc-markdown table { display: table; table-layout: fixed; width: 100%; } html[class*='docs-doc-id-zcp'] .theme-doc-markdown table th, - html[class*='docs-doc-id-zcp'] .theme-doc-markdown table td { + html[class*='docs-doc-id-zcp'] .theme-doc-markdown table td, + html[class*='docs-doc-id-features/coding-agents'] .theme-doc-markdown table th, + html[class*='docs-doc-id-features/coding-agents'] .theme-doc-markdown table td, + html[class*='docs-doc-id-features/local-remote-development'] .theme-doc-markdown table th, + html[class*='docs-doc-id-features/local-remote-development'] .theme-doc-markdown table td { overflow-wrap: anywhere; word-break: normal; vertical-align: top;